From aa4283c7eac306448c1f780eb16a4ab34c8f381a Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Wed, 30 Jul 2025 18:13:55 +0800 Subject: [PATCH] update with inventory import --- .../modules/master/service/BomService.kt | 6 +- .../purchaseOrder/entity/PurchaseOrderLine.kt | 1 - .../purchaseOrder/enums/PurchaseOrderEnum.kt | 1 + .../modules/stock/service/InventoryService.kt | 272 +++++++++++++++++- .../01_update_pol_set_m18LogId_null.sql | 5 + 5 files changed, 276 insertions(+), 9 deletions(-) create mode 100644 src/main/resources/db/changelog/changes/20250730_01_derek/01_update_pol_set_m18LogId_null.sql 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 63d4748..65eb645 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 @@ -187,12 +187,12 @@ open class BomService( // println(item) bomMatRequest.item = item } - 2 -> { - bomMatRequest.qty = tempCell.numericCellValue.toBigDecimal() - } 3 -> { bomMatRequest.uomName = tempCell.stringCellValue.trim() } + 6 -> { + bomMatRequest.qty = tempCell.numericCellValue.toBigDecimal() + } 7 -> { val salesUnit = uomConversionRepository.findByCodeAndDeletedFalse(tempCell.stringCellValue.trim()) bomMatRequest.salesUnit = salesUnit diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt index 2196af4..28044f0 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt @@ -44,7 +44,6 @@ open class PurchaseOrderLine : BaseEntity() { @Column(name = "status", nullable = false, length = 10) open var status: PurchaseOrderLineStatus? = null - @NotNull @ManyToOne @JoinColumn(name = "m18DataLogId", nullable = false) open var m18DataLog: M18DataLog? = null diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderEnum.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderEnum.kt index 2e32efa..65128ea 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderEnum.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/enums/PurchaseOrderEnum.kt @@ -16,6 +16,7 @@ enum class PurchaseOrderStatus(val value: String) { } enum class PurchaseOrderType(val value: String) { + IMPORT ("import"), MATERIAL ("material"), SHOP ("shop"), OEM ("oem") diff --git a/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt b/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt index 161deff..ada9713 100644 --- a/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt +++ b/src/main/java/com/ffii/fpsms/modules/stock/service/InventoryService.kt @@ -3,17 +3,32 @@ package com.ffii.fpsms.modules.stock.service import com.ffii.core.response.RecordsRes import com.ffii.core.support.AbstractBaseEntityService import com.ffii.core.support.JdbcDao -import com.ffii.fpsms.modules.master.entity.Items -import com.ffii.fpsms.modules.master.entity.ItemsRepository -import com.ffii.fpsms.modules.master.entity.UomConversionRepository +import com.ffii.fpsms.modules.common.CodeGenerator +import com.ffii.fpsms.modules.master.entity.* import com.ffii.fpsms.modules.master.service.ItemUomService import com.ffii.fpsms.modules.master.service.UomConversionService -import com.ffii.fpsms.modules.stock.entity.Inventory -import com.ffii.fpsms.modules.stock.entity.InventoryRepository +import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrder +import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLine +import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLineRepository +import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderRepository +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderLineStatus +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderStatus +import com.ffii.fpsms.modules.purchaseOrder.enums.PurchaseOrderType +import com.ffii.fpsms.modules.stock.entity.* +import com.ffii.fpsms.modules.stock.entity.enum.InventoryLotLineStatus import com.ffii.fpsms.modules.stock.entity.projection.InventoryInfo import com.ffii.fpsms.modules.stock.web.model.SearchInventoryRequest +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.support.PathMatchingResourcePatternResolver import org.springframework.data.domain.PageRequest import org.springframework.stereotype.Service +import java.math.BigDecimal +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter @Service open class InventoryService( @@ -23,6 +38,14 @@ open class InventoryService( private val uomConversionService: UomConversionService, private val uomConversionRepository: UomConversionRepository, private val itemUomService: ItemUomService, + private val purchaseOrderRepository: PurchaseOrderRepository, + private val purchaseOrderLineRepository: PurchaseOrderLineRepository, + private val currencyRepository: CurrencyRepository, + private val stockInRepository: StockInRepository, + private val stockInLineRepository: StockInLineRepository, + private val inventoryLotRepository: InventoryLotRepository, + private val inventoryLotLineRepository: InventoryLotLineRepository, + private val warehouseRepository: WarehouseRepository ): AbstractBaseEntityService(jdbcDao, inventoryRepository) { open fun allInventory(): List { // TODO: Replace by actual logic @@ -58,6 +81,245 @@ open class InventoryService( return inventoryRepository.findInventoryInfoByItemIdInAndDeletedIsFalse(itemIds); } + fun save_po(): PurchaseOrder { + val poCode = "MANUAL-IMPORT ${LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)}" + val po = PurchaseOrder().apply { + this.code = poCode + this.currency = currencyRepository.findByIdAndDeletedIsFalse(1) + this.orderDate = LocalDate.now().atStartOfDay() + this.estimatedArrivalDate = LocalDate.now().atStartOfDay() + this.completeDate = LocalDate.now().atStartOfDay() + this.status = PurchaseOrderStatus.COMPLETED + this.type = PurchaseOrderType.IMPORT + // set m18Datalog to nullable + } + return purchaseOrderRepository.saveAndFlush(po) + } + + data class InventoryImportExcelField( + val itemCode: String, + val expiryDate: LocalDate, + val qty: BigDecimal, + val warehouseCode: String, + ) + + fun save_stock_in(po: PurchaseOrder): StockIn { + val stockIn = StockIn().apply { + this.code = po.code + this.orderDate = po.orderDate + this.estimatedCompleteDate = LocalDate.now() + this.completeDate = LocalDateTime.now() + this.purchaseOrder = po + } + return stockInRepository.save(stockIn) + } + fun save_stock_in_line(po: PurchaseOrder, stockIn: StockIn, pol: MutableList) { + val entries = pol.map { + StockInLine() + } + } + fun batch_save(sheet: Sheet, po: PurchaseOrder) { + var START_ROW_INDEX = 1 + var START_COLUMN_INDEX = 1 + val checkColumnIndex = 1 + val itemCodeColumnIndex = 1 + val expiryDateColumnIndex = 2 + val qtyColumnIndex = 3 + val warehouseColumnIndex = 4 + val columnList = listOf( + itemCodeColumnIndex, + expiryDateColumnIndex, + qtyColumnIndex, + warehouseColumnIndex, + ) + var counter = 0 + val BATCH_SIZE = 50 + val entriesList = mutableListOf() + while (true) { + val currentRow = sheet.getRow(START_ROW_INDEX) + val itemCell = currentRow.getCell(itemCodeColumnIndex) + // do save while ch + if (entriesList.size == BATCH_SIZE || itemCell == null) { + if (entriesList.isEmpty()) break + val polEntries = purchaseOrderLineRepository.saveAllAndFlush(entriesList) + val stockIn = save_stock_in(po) + // do save sil + save_stock_in_line( + po = po, + stockIn = stockIn, + pol = polEntries + ) + entriesList.clear() + if (itemCell == null) break + } + val expiryDateCell = currentRow.getCell(expiryDateColumnIndex) + val qtyCell = currentRow.getCell(qtyColumnIndex) + val warehouseCell = currentRow.getCell(warehouseColumnIndex) + val request = InventoryImportExcelField( + itemCode = itemCell.stringCellValue, + expiryDate = itemCell.dateCellValue + .toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(), + qty = qtyCell.numericCellValue.toBigDecimal(), + warehouseCode = warehouseCell.stringCellValue + ) + val item = itemsRepository.findByCodeAndDeletedFalse(request.itemCode) + val salesUnit = itemUomService.findSalesUnitByItemId(itemId = item!!.id!!)!!.uom + val purchaseOrderLine = PurchaseOrderLine() + .apply { + this.item = item + this.itemNo = item.code + this.uom = salesUnit + this.purchaseOrder = po + this.price = BigDecimal.ZERO + this.status = PurchaseOrderLineStatus.COMPLETED + } + entriesList.add(purchaseOrderLine) + + + START_ROW_INDEX++ + counter++ + } + } + open fun importInventoryTestingMethod() { + val resolver = PathMatchingResourcePatternResolver() + val TARGET_LOCATION_FILE = "file:C:/Users/2Fi/Desktop/Fourth Wave of BOM Excel/*.xlsx" // CHANGE THIS FOR DIFFERENT FILE + val excel = resolver.getResource(TARGET_LOCATION_FILE) + val inputStream = excel.inputStream + val workbook: Workbook = XSSFWorkbook(inputStream) + val sheet: Sheet = workbook.getSheetAt(0) + + val po = save_po() + // saving lines in batch + batch_save(sheet = sheet, po = po) + } + + data class ExcelMap( + val itemCodeColumnIndex: Int, + val expiryDateColumnIndex: Int, + val qtyColumnIndex: Int, + val warehouseColumnIndex: Int, + ) + open fun importInventoryExcel() { + val resolver = PathMatchingResourcePatternResolver() + val TARGET_LOCATION_FILE = "file:C:/Users/2Fi/Desktop/Fourth Wave of BOM Excel/*.xlsx" // CHANGE THIS FOR DIFFERENT FILE + val excel = resolver.getResource(TARGET_LOCATION_FILE) + val inputStream = excel.inputStream + val workbook: Workbook = XSSFWorkbook(inputStream) + val sheet: Sheet = workbook.getSheetAt(0) + + var START_ROW_INDEX = 1 + var START_COLUMN_INDEX = 1 + val columnMap = ExcelMap( + 1, + 2, + 3, + 4 + ) + val checkingRow = sheet.getRow(START_ROW_INDEX) + val checkingFirstCell = checkingRow.getCell(columnMap.itemCodeColumnIndex) + val purchaseOrderLineList = mutableListOf() + val requestList = mutableListOf() + // purchase order + val po = save_po() + // purchase order mostly + try { + while (checkingFirstCell != null) { + val currentRow = sheet.getRow(START_ROW_INDEX) + val itemCell = currentRow.getCell(columnMap.itemCodeColumnIndex) + val expiryDateCell = currentRow.getCell(columnMap.expiryDateColumnIndex) + val qtyCell = currentRow.getCell(columnMap.qtyColumnIndex) + val warehouseCell = currentRow.getCell(columnMap.warehouseColumnIndex) + // info + val request = InventoryImportExcelField( + itemCode = itemCell.stringCellValue, + expiryDate = itemCell.dateCellValue + .toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(), + qty = qtyCell.numericCellValue.toBigDecimal(), + warehouseCode = warehouseCell.stringCellValue + ) + requestList.add(request) + val item = itemsRepository.findByCodeAndDeletedFalse(request.itemCode) + val salesUnit = itemUomService.findSalesUnitByItemId(itemId = item!!.id!!)!!.uom + // purchase order line + val purchaseOrderLine = PurchaseOrderLine() + .apply { + this.item = item + this.itemNo = item.code + this.uom = salesUnit + this.purchaseOrder = po + this.price = BigDecimal.ZERO + this.status = PurchaseOrderLineStatus.COMPLETED + } + purchaseOrderLineList.add(purchaseOrderLine) + START_ROW_INDEX++ + } + } catch (e:Error) { + // write log to pol excel + } + + val polList = purchaseOrderLineRepository.saveAllAndFlush(purchaseOrderLineList) + // stock in + val stockIn = save_stock_in(po) + // stock in line + val oneYearExpiry = 1L + val stockInLineEntries = polList.map { pol -> + val newLotNo = CodeGenerator.generateCode( + prefix = "POLOT", + itemId = pol.item!!.id!!, + count = pol.id!!.toInt() + ) + StockInLine().apply { + this.purchaseOrder = po + this.purchaseOrderLine = pol + this.item = pol.item + this.itemNo = pol.item!!.code + this.stockIn = stockIn + this.acceptedQty = pol.qty + this.receiptDate = LocalDateTime.now() + this.expiryDate = LocalDate.now().plusYears(oneYearExpiry) // default + this.lotNo = newLotNo + } + } + val savedStockInLineEntries = stockInLineRepository.saveAllAndFlush(stockInLineEntries) + // inventory lot + val inventoryLotEntries = savedStockInLineEntries.map { stockInLine -> + InventoryLot().apply { + this.item = stockInLine.item + this.stockInLine = stockInLine + this.stockInDate = LocalDateTime.now() + this.expiryDate = stockInLine.expiryDate + this.lotNo = stockInLine.lotNo + } + } + val savedInventoryLotEntries = inventoryLotRepository.saveAllAndFlush(inventoryLotEntries) + // inventory lot line +// val inventoryLotLIneEntries = inventoryLotEntries.map {inventoryLot -> + val inventoryLotLineEntries = requestList.mapIndexed { index, request -> + val inventoryLot = savedInventoryLotEntries[index] + val warehouse = warehouseRepository.findAll().find { it.code == request.warehouseCode }!! + val salesUnit = itemUomService.findSalesUnitByItemId(itemId = inventoryLot.item!!.id!!) + InventoryLotLine().apply { + this.inventoryLot = inventoryLot + this.warehouse = warehouse + this.inQty = request.qty + this.status = InventoryLotLineStatus.AVAILABLE + this.stockUom = salesUnit + } + } + val savedInventoryLotLine = inventoryLotLineRepository.saveAllAndFlush(inventoryLotLineEntries) + val updateStockInLineEntries = savedInventoryLotLine.mapIndexed { index, inventoryLotLine -> + savedStockInLineEntries[index].apply { + this.inventoryLotLine = inventoryLotLine + this.inventoryLot = inventoryLotLine.inventoryLot + } + } + val savedNewStockInLineEntries = stockInLineRepository.saveAllAndFlush(updateStockInLineEntries) + + } // @Throws(IOException::class) // open fun updateInventory(request: SaveInventoryRequest): MessageResponse { diff --git a/src/main/resources/db/changelog/changes/20250730_01_derek/01_update_pol_set_m18LogId_null.sql b/src/main/resources/db/changelog/changes/20250730_01_derek/01_update_pol_set_m18LogId_null.sql new file mode 100644 index 0000000..2f8359c --- /dev/null +++ b/src/main/resources/db/changelog/changes/20250730_01_derek/01_update_pol_set_m18LogId_null.sql @@ -0,0 +1,5 @@ +-- liquibase formatted sql +-- changeset derek:update_pol_set_m18LogId_null + +ALTER TABLE `purchase_order_line` + MODIFY COLUMN `m18DataLogId` Int(11) NULL; \ No newline at end of file