| @@ -33,11 +33,14 @@ import java.io.OutputStream | |||
| import java.io.UnsupportedEncodingException | |||
| import java.text.ParseException | |||
| import java.time.LocalDateTime | |||
| import com.ffii.fpsms.modules.deliveryOrder.web.models.PrintQrCodeForDoRequest | |||
| import com.ffii.fpsms.modules.stock.service.StockInLineService | |||
| @RequestMapping("/do") | |||
| @RestController | |||
| class DeliveryOrderController( | |||
| private val deliveryOrderService: DeliveryOrderService, | |||
| private val stockInLineService: StockInLineService, | |||
| ) { | |||
| @GetMapping("/list") | |||
| fun getDoList(): List<DeliveryOrderInfo> { | |||
| @@ -217,4 +220,9 @@ class DeliveryOrderController( | |||
| fun printDN(@ModelAttribute request: PrintDNLabelsRequest) { | |||
| 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 com.ffii.fpsms.modules.stock.entity.Inventory | |||
| 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.StockIn | |||
| import com.ffii.fpsms.modules.stock.entity.StockInLineRepository | |||
| @@ -61,6 +62,7 @@ open class ItemsService( | |||
| private val stockInLineService: StockInLineService, | |||
| private val stockInLineRepository: StockInLineRepository, | |||
| private val inventoryLotLineRepository: InventoryLotLineRepository, | |||
| private val inventoryLotRepository: InventoryLotRepository, | |||
| ): AbstractBaseEntityService<Items, Long, ItemsRepository>(jdbcDao, itemsRepository) { | |||
| private val excelImportPath: String = System.getProperty("user.home") + "/Downloads/StockTakeImport/" | |||
| @@ -908,9 +910,12 @@ open class ItemsService( | |||
| // Calculate expiry date (+30 days) | |||
| 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 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 | |||
| val dnNo = "DN-$dateStr-$itemCode" | |||
| @@ -924,7 +929,8 @@ open class ItemsService( | |||
| expiryDate = expiryDate, | |||
| warehouseId = warehouse?.id, | |||
| stockTakeLineId = savedStockTakeLine.id, | |||
| dnNo = dnNo, // Set dnNo | |||
| dnNo = dnNo, | |||
| productLotNo = productLotNo, // LT-YYYYMMDD-itemCode | |||
| qcAccept = true, | |||
| status = StockInLineStatus.PENDING.status | |||
| ) | |||
| @@ -970,6 +976,7 @@ open class ItemsService( | |||
| id = savedStockInLine.id | |||
| status = StockInLineStatus.PENDING.status | |||
| this.dnNo = dnNo | |||
| this.productLotNo = lotNo | |||
| this.qcAccept = true | |||
| this.acceptedQty = qty | |||
| this.acceptQty = qty | |||
| @@ -1002,7 +1009,14 @@ open class ItemsService( | |||
| logger.warn("Row ${i + 1}: InventoryLot was not created for StockInLine ${updatedForLot.id}") | |||
| logWriter.writeRowError(i, "InventoryLot was not created") | |||
| } 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 | |||
| @@ -1017,6 +1031,7 @@ open class ItemsService( | |||
| id = savedStockInLine.id | |||
| status = StockInLineStatus.RECEIVED.status // Explicitly set to RECEIVED | |||
| this.dnNo = dnNo | |||
| this.productLotNo = productLotNo | |||
| this.acceptedQty = qty | |||
| this.acceptQty = qty | |||
| 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.fpsms.modules.stock.entity.projection.QrCodeInfo | |||
| import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo | |||
| import org.springframework.data.jpa.repository.Query | |||
| import org.springframework.stereotype.Repository | |||
| import java.util.Optional | |||
| @@ -19,4 +20,7 @@ interface StockInLineRepository : AbstractRepository<StockInLine, Long> { | |||
| fun findAllByPurchaseOrderIdAndDeletedFalse(purchaseOrderId: Long): Optional<List<StockInLine>> | |||
| fun findStockInLineInfoByInventoryLotLineId(inventoryLotLineId: 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 java.io.File | |||
| 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 | |||
| data class QrContent(val itemId: Long, val stockInLineId: Long) | |||
| @@ -71,6 +72,7 @@ open class StockInLineService( | |||
| private val itemUomRepository: ItemUomRespository, | |||
| private val printerService: PrinterService, | |||
| private val stockTakeLineRepository: StockTakeLineRepository, | |||
| private val deliveryOrderRepository: DeliveryOrderRepository, | |||
| ): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) { | |||
| open fun getStockInLineInfo(stockInLineId: Long): StockInLineInfo { | |||
| @@ -577,4 +579,30 @@ open class StockInLineService( | |||
| 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?, | |||
| ) | |||