|
@@ -3,17 +3,32 @@ package com.ffii.fpsms.modules.stock.service |
|
|
import com.ffii.core.response.RecordsRes |
|
|
import com.ffii.core.response.RecordsRes |
|
|
import com.ffii.core.support.AbstractBaseEntityService |
|
|
import com.ffii.core.support.AbstractBaseEntityService |
|
|
import com.ffii.core.support.JdbcDao |
|
|
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.ItemUomService |
|
|
import com.ffii.fpsms.modules.master.service.UomConversionService |
|
|
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.entity.projection.InventoryInfo |
|
|
import com.ffii.fpsms.modules.stock.web.model.SearchInventoryRequest |
|
|
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.data.domain.PageRequest |
|
|
import org.springframework.stereotype.Service |
|
|
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 |
|
|
@Service |
|
|
open class InventoryService( |
|
|
open class InventoryService( |
|
@@ -23,6 +38,14 @@ open class InventoryService( |
|
|
private val uomConversionService: UomConversionService, |
|
|
private val uomConversionService: UomConversionService, |
|
|
private val uomConversionRepository: UomConversionRepository, |
|
|
private val uomConversionRepository: UomConversionRepository, |
|
|
private val itemUomService: ItemUomService, |
|
|
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<Inventory, Long, InventoryRepository>(jdbcDao, inventoryRepository) { |
|
|
): AbstractBaseEntityService<Inventory, Long, InventoryRepository>(jdbcDao, inventoryRepository) { |
|
|
open fun allInventory(): List<Inventory> { |
|
|
open fun allInventory(): List<Inventory> { |
|
|
// TODO: Replace by actual logic |
|
|
// TODO: Replace by actual logic |
|
@@ -58,6 +81,245 @@ open class InventoryService( |
|
|
return inventoryRepository.findInventoryInfoByItemIdInAndDeletedIsFalse(itemIds); |
|
|
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<PurchaseOrderLine>) { |
|
|
|
|
|
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<PurchaseOrderLine>() |
|
|
|
|
|
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<PurchaseOrderLine>() |
|
|
|
|
|
val requestList = mutableListOf<InventoryImportExcelField>() |
|
|
|
|
|
// 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) |
|
|
// @Throws(IOException::class) |
|
|
// open fun updateInventory(request: SaveInventoryRequest): MessageResponse { |
|
|
// open fun updateInventory(request: SaveInventoryRequest): MessageResponse { |
|
|