| @@ -33,11 +33,14 @@ import java.io.OutputStream | |||||
| import java.io.UnsupportedEncodingException | import java.io.UnsupportedEncodingException | ||||
| import java.text.ParseException | import java.text.ParseException | ||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintQrCodeForDoRequest | |||||
| import com.ffii.fpsms.modules.stock.service.StockInLineService | |||||
| @RequestMapping("/do") | @RequestMapping("/do") | ||||
| @RestController | @RestController | ||||
| class DeliveryOrderController( | class DeliveryOrderController( | ||||
| private val deliveryOrderService: DeliveryOrderService, | private val deliveryOrderService: DeliveryOrderService, | ||||
| private val stockInLineService: StockInLineService, | |||||
| ) { | ) { | ||||
| @GetMapping("/list") | @GetMapping("/list") | ||||
| fun getDoList(): List<DeliveryOrderInfo> { | fun getDoList(): List<DeliveryOrderInfo> { | ||||
| @@ -217,4 +220,9 @@ class DeliveryOrderController( | |||||
| fun printDN(@ModelAttribute request: PrintDNLabelsRequest) { | fun printDN(@ModelAttribute request: PrintDNLabelsRequest) { | ||||
| deliveryOrderService.printDNLabels(request) | deliveryOrderService.printDNLabels(request) | ||||
| } | } | ||||
| @GetMapping("/batchPrintQrCode") | |||||
| fun printQrCodeForDeliveryOrder(@ModelAttribute request: PrintQrCodeForDoRequest) { | |||||
| stockInLineService.printQrCodeForDeliveryOrder(request) | |||||
| } | |||||
| } | } | ||||
| @@ -25,6 +25,7 @@ import java.io.FileInputStream | |||||
| import java.math.BigDecimal | import java.math.BigDecimal | ||||
| import com.ffii.fpsms.modules.stock.entity.Inventory | import com.ffii.fpsms.modules.stock.entity.Inventory | ||||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository | import com.ffii.fpsms.modules.stock.entity.InventoryLotLineRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository | |||||
| import com.ffii.fpsms.modules.stock.entity.InventoryRepository | import com.ffii.fpsms.modules.stock.entity.InventoryRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.StockIn | import com.ffii.fpsms.modules.stock.entity.StockIn | ||||
| import com.ffii.fpsms.modules.stock.entity.StockInLineRepository | import com.ffii.fpsms.modules.stock.entity.StockInLineRepository | ||||
| @@ -61,6 +62,7 @@ open class ItemsService( | |||||
| private val stockInLineService: StockInLineService, | private val stockInLineService: StockInLineService, | ||||
| private val stockInLineRepository: StockInLineRepository, | private val stockInLineRepository: StockInLineRepository, | ||||
| private val inventoryLotLineRepository: InventoryLotLineRepository, | private val inventoryLotLineRepository: InventoryLotLineRepository, | ||||
| private val inventoryLotRepository: InventoryLotRepository, | |||||
| ): AbstractBaseEntityService<Items, Long, ItemsRepository>(jdbcDao, itemsRepository) { | ): AbstractBaseEntityService<Items, Long, ItemsRepository>(jdbcDao, itemsRepository) { | ||||
| private val excelImportPath: String = System.getProperty("user.home") + "/Downloads/StockTakeImport/" | private val excelImportPath: String = System.getProperty("user.home") + "/Downloads/StockTakeImport/" | ||||
| @@ -908,9 +910,12 @@ open class ItemsService( | |||||
| // Calculate expiry date (+30 days) | // Calculate expiry date (+30 days) | ||||
| val expiryDate = LocalDateTime.now().plusDays(30).toLocalDate() | val expiryDate = LocalDateTime.now().plusDays(30).toLocalDate() | ||||
| // Generate lotNo: LT-YYYYMMDD-itemCode | |||||
| // Generate productLotNo: LT-YYYYMMDD-itemCode (for productLotNo field) | |||||
| val dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) | val dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) | ||||
| val lotNo = "LT-$dateStr-$itemCode" | |||||
| val productLotNo = "LT-$dateStr-$itemCode" | |||||
| // Generate lotNo: LT-YYYYMMDD-XXXX (sequential number, same for StockInLine and InventoryLot) | |||||
| val lotNo = stockInLineService.assignLotNo() | |||||
| // Generate dnNo: DN-YYYYMMDD-itemCode | // Generate dnNo: DN-YYYYMMDD-itemCode | ||||
| val dnNo = "DN-$dateStr-$itemCode" | val dnNo = "DN-$dateStr-$itemCode" | ||||
| @@ -924,7 +929,8 @@ open class ItemsService( | |||||
| expiryDate = expiryDate, | expiryDate = expiryDate, | ||||
| warehouseId = warehouse?.id, | warehouseId = warehouse?.id, | ||||
| stockTakeLineId = savedStockTakeLine.id, | stockTakeLineId = savedStockTakeLine.id, | ||||
| dnNo = dnNo, // Set dnNo | |||||
| dnNo = dnNo, | |||||
| productLotNo = productLotNo, // LT-YYYYMMDD-itemCode | |||||
| qcAccept = true, | qcAccept = true, | ||||
| status = StockInLineStatus.PENDING.status | status = StockInLineStatus.PENDING.status | ||||
| ) | ) | ||||
| @@ -970,6 +976,7 @@ open class ItemsService( | |||||
| id = savedStockInLine.id | id = savedStockInLine.id | ||||
| status = StockInLineStatus.PENDING.status | status = StockInLineStatus.PENDING.status | ||||
| this.dnNo = dnNo | this.dnNo = dnNo | ||||
| this.productLotNo = lotNo | |||||
| this.qcAccept = true | this.qcAccept = true | ||||
| this.acceptedQty = qty | this.acceptedQty = qty | ||||
| this.acceptQty = qty | this.acceptQty = qty | ||||
| @@ -1002,7 +1009,14 @@ open class ItemsService( | |||||
| logger.warn("Row ${i + 1}: InventoryLot was not created for StockInLine ${updatedForLot.id}") | logger.warn("Row ${i + 1}: InventoryLot was not created for StockInLine ${updatedForLot.id}") | ||||
| logWriter.writeRowError(i, "InventoryLot was not created") | logWriter.writeRowError(i, "InventoryLot was not created") | ||||
| } else { | } else { | ||||
| logger.info("Row ${i + 1}: InventoryLot created (ID: ${refreshedStockInLine.inventoryLot?.id}, lotNo: ${refreshedStockInLine.inventoryLot?.lotNo})") | |||||
| // Update InventoryLot's lotNo to match StockInLine's lotNo (LT-YYYYMMDD-XXXX format) | |||||
| val inventoryLot = refreshedStockInLine.inventoryLot!! | |||||
| if (inventoryLot.lotNo != lotNo) { | |||||
| inventoryLot.lotNo = lotNo | |||||
| inventoryLotRepository.saveAndFlush(inventoryLot) | |||||
| logger.info("Row ${i + 1}: Updated InventoryLot lotNo to '$lotNo' to match StockInLine (InventoryLot ID: ${inventoryLot.id})") | |||||
| } | |||||
| logger.info("Row ${i + 1}: InventoryLot created (ID: ${inventoryLot.id}, lotNo: ${inventoryLot.lotNo})") | |||||
| } | } | ||||
| // Step 7b: Second update to RECEIVED with inventoryLotLines to create InventoryLotLine | // Step 7b: Second update to RECEIVED with inventoryLotLines to create InventoryLotLine | ||||
| @@ -1017,6 +1031,7 @@ open class ItemsService( | |||||
| id = savedStockInLine.id | id = savedStockInLine.id | ||||
| status = StockInLineStatus.RECEIVED.status // Explicitly set to RECEIVED | status = StockInLineStatus.RECEIVED.status // Explicitly set to RECEIVED | ||||
| this.dnNo = dnNo | this.dnNo = dnNo | ||||
| this.productLotNo = productLotNo | |||||
| this.acceptedQty = qty | this.acceptedQty = qty | ||||
| this.acceptQty = qty | this.acceptQty = qty | ||||
| this.inventoryLotLines = inventoryLotLines // REQUIRED for InventoryLotLine creation | this.inventoryLotLines = inventoryLotLines // REQUIRED for InventoryLotLine creation | ||||
| @@ -3,6 +3,7 @@ package com.ffii.fpsms.modules.stock.entity | |||||
| import com.ffii.core.support.AbstractRepository | import com.ffii.core.support.AbstractRepository | ||||
| import com.ffii.fpsms.modules.stock.entity.projection.QrCodeInfo | import com.ffii.fpsms.modules.stock.entity.projection.QrCodeInfo | ||||
| import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo | import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo | ||||
| import org.springframework.data.jpa.repository.Query | |||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||
| import java.util.Optional | import java.util.Optional | ||||
| @@ -19,4 +20,7 @@ interface StockInLineRepository : AbstractRepository<StockInLine, Long> { | |||||
| fun findAllByPurchaseOrderIdAndDeletedFalse(purchaseOrderId: Long): Optional<List<StockInLine>> | fun findAllByPurchaseOrderIdAndDeletedFalse(purchaseOrderId: Long): Optional<List<StockInLine>> | ||||
| fun findStockInLineInfoByInventoryLotLineId(inventoryLotLineId: Long): Optional<StockInLineInfo> | fun findStockInLineInfoByInventoryLotLineId(inventoryLotLineId: Long): Optional<StockInLineInfo> | ||||
| fun findStockInLineInfoById(id: Long): Optional<StockInLineInfo> | fun findStockInLineInfoById(id: Long): Optional<StockInLineInfo> | ||||
| @Query("SELECT sil FROM StockInLine sil WHERE sil.item.id = :itemId AND sil.deleted = false") | |||||
| fun findAllByItemIdAndDeletedFalse(itemId: Long): List<StockInLine> | |||||
| } | } | ||||
| @@ -46,7 +46,8 @@ import net.sf.jasperreports.engine.JasperExportManager | |||||
| import net.sf.jasperreports.engine.JasperPrint | import net.sf.jasperreports.engine.JasperPrint | ||||
| import java.io.File | import java.io.File | ||||
| import kotlin.jvm.optionals.getOrNull | import kotlin.jvm.optionals.getOrNull | ||||
| import kotlin.math.max | |||||
| import com.ffii.fpsms.modules.deliveryOrder.entity.DeliveryOrderRepository | |||||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintQrCodeForDoRequest | |||||
| @Serializable | @Serializable | ||||
| data class QrContent(val itemId: Long, val stockInLineId: Long) | data class QrContent(val itemId: Long, val stockInLineId: Long) | ||||
| @@ -71,6 +72,7 @@ open class StockInLineService( | |||||
| private val itemUomRepository: ItemUomRespository, | private val itemUomRepository: ItemUomRespository, | ||||
| private val printerService: PrinterService, | private val printerService: PrinterService, | ||||
| private val stockTakeLineRepository: StockTakeLineRepository, | private val stockTakeLineRepository: StockTakeLineRepository, | ||||
| private val deliveryOrderRepository: DeliveryOrderRepository, | |||||
| ): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | ): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | ||||
| open fun getStockInLineInfo(stockInLineId: Long): StockInLineInfo { | open fun getStockInLineInfo(stockInLineId: Long): StockInLineInfo { | ||||
| @@ -577,4 +579,30 @@ open class StockInLineService( | |||||
| tempPdfFile.delete() | tempPdfFile.delete() | ||||
| } | } | ||||
| } | } | ||||
| @Transactional | |||||
| open fun printQrCodeForDeliveryOrder(request: PrintQrCodeForDoRequest) { | |||||
| // Get delivery order by ID | |||||
| val deliveryOrder = deliveryOrderRepository.findByIdAndDeletedIsFalse(request.deliveryOrderId) | |||||
| ?: throw NoSuchElementException("Delivery order not found with ID: ${request.deliveryOrderId}") | |||||
| // Loop through delivery order lines | |||||
| deliveryOrder.deliveryOrderLines.forEach { deliveryOrderLine -> | |||||
| val itemId = deliveryOrderLine.item?.id | |||||
| ?: throw IllegalStateException("Delivery order line ${deliveryOrderLine.id} has no item") | |||||
| // Find all stock in lines matching this itemId | |||||
| val stockInLines = stockInLineRepository.findAllByItemIdAndDeletedFalse(itemId) | |||||
| // Print QR code for each stock in line | |||||
| stockInLines.forEach { stockInLine -> | |||||
| val printRequest = PrintQrCodeForSilRequest( | |||||
| stockInLineId = stockInLine.id!!, | |||||
| printerId = request.printerId, | |||||
| printQty = request.printQty | |||||
| ) | |||||
| printQrCode(printRequest) | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,7 @@ | |||||
| package com.ffii.fpsms.modules.deliveryOrder.web.models | |||||
| data class PrintQrCodeForDoRequest( | |||||
| val deliveryOrderId: Long, | |||||
| val printerId: Long, | |||||
| val printQty: Int?, | |||||
| ) | |||||