Quellcode durchsuchen

update put away

master
cyril.tsui vor 9 Stunden
Ursprung
Commit
be506de370
23 geänderte Dateien mit 353 neuen und 51 gelöschten Zeilen
  1. +52
    -12
      src/main/java/com/ffii/core/utils/ZebraPrinterUtil.kt
  2. +1
    -1
      src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt
  3. +1
    -2
      src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt
  4. +32
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/Printer.kt
  5. +13
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/PrinterRepository.kt
  6. +2
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/WarehouseRepository.kt
  7. +16
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/projections/PrinterCombo.kt
  8. +11
    -0
      src/main/java/com/ffii/fpsms/modules/master/entity/projections/WarehouseCombo.kt
  9. +19
    -0
      src/main/java/com/ffii/fpsms/modules/master/service/PrinterService.kt
  10. +5
    -0
      src/main/java/com/ffii/fpsms/modules/master/service/WarehouseService.kt
  11. +18
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/PrinterController.kt
  12. +1
    -3
      src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt
  13. +6
    -0
      src/main/java/com/ffii/fpsms/modules/master/web/WarehouseController.kt
  14. +3
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt
  15. +6
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLot.kt
  16. +2
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt
  17. +2
    -2
      src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt
  18. +19
    -3
      src/main/java/com/ffii/fpsms/modules/stock/entity/projection/StockInLineInfo.kt
  19. +104
    -26
      src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt
  20. +6
    -1
      src/main/java/com/ffii/fpsms/modules/stock/web/StockInLineController.kt
  21. +8
    -0
      src/main/java/com/ffii/fpsms/modules/stock/web/model/PrintQrCodeRequest.kt
  22. +7
    -1
      src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt
  23. +19
    -0
      src/main/resources/db/changelog/changes/20250825_01_cyril/02_create_printer.sql

+ 52
- 12
src/main/java/com/ffii/core/utils/ZebraPrinterUtil.kt Datei anzeigen

@@ -3,6 +3,7 @@ package com.ffii.core.utils
import org.apache.pdfbox.pdmodel.PDDocument
import org.apache.pdfbox.rendering.PDFRenderer
import org.apache.pdfbox.rendering.ImageType
import java.awt.Graphics2D
import java.awt.image.BufferedImage
import java.io.File
import java.io.OutputStream
@@ -15,18 +16,27 @@ import java.util.*
*/
open class ZebraPrinterUtil {

companion object {
// Enum to represent valid ZPL print directions
enum class PrintDirection(val zplCode: String, val degree: Double) {
NORMAL("N", 0.0), // Normal (0 degrees)
ROTATED("R", 90.0), // Rotated 90 degrees clockwise
INVERTED("I", 180.0), // Inverted 180 degrees
BOTTOM_UP("B", 270.0) // Bottom-up, 270 degrees clockwise
}

companion object {
/**
* Converts the first page of a PDF document into a ZPL command and sends it to the specified printer.
*
* @param pdfFile The PDF file to be printed.
* @param printerIp The IP address of the Zebra printer.
* @param printerPort The port of the Zebra printer, typically 9100.
* @param printQty The qty of print, default 1
* @param printDirection Valid values: N (Normal), R (Rotated 90), I (Inverted 180), B (Bottom-up 270)
* @throws Exception if there is an error during file processing or printing.
*/
@JvmStatic
fun printPdfToZebra(pdfFile: File, printerIp: String, printerPort: Int) {
fun printPdfToZebra(pdfFile: File, printerIp: String, printerPort: Int, printQty: Int? = 1, printDirection: PrintDirection = PrintDirection.NORMAL) {

// Check if the file exists and is readable
if (!pdfFile.exists() || !pdfFile.canRead()) {
@@ -43,14 +53,16 @@ open class ZebraPrinterUtil {
val image = renderer.renderImage(0, 300 / 72f, ImageType.BINARY)

// 3. Convert the image to a ZPL format string
val zplCommand = convertImageToZpl(image)
val zplCommand = convertImageToZpl(image, printDirection)

// 4. Send the ZPL command to the printer via a network socket
val printData = zplCommand.toByteArray()
Socket(printerIp, printerPort).use { socket ->
val os: OutputStream = socket.getOutputStream()
os.write(printData)
os.flush()
repeat(printQty ?: 1) { index ->
Socket(printerIp, printerPort).use { socket ->
val os: OutputStream = socket.getOutputStream()
os.write(printData)
os.flush()
}
}
}
} catch (e: Exception) {
@@ -59,32 +71,60 @@ open class ZebraPrinterUtil {
}
}

/**
* Rotates a BufferedImage by the specified angle.
*
* @param image The BufferedImage to rotate.
* @param angleDegrees The rotation angle in degrees (clockwise).
* @return The rotated BufferedImage.
*/
private fun rotateImage(image: BufferedImage, angleDegrees: Double): BufferedImage {
val rads = Math.toRadians(angleDegrees)
val sin = Math.abs(Math.sin(rads))
val cos = Math.abs(Math.cos(rads))
val w = image.width
val h = image.height
val newWidth = (w * cos + h * sin).toInt()
val newHeight = (w * sin + h * cos).toInt()

val rotated = BufferedImage(newWidth, newHeight, BufferedImage.TYPE_BYTE_BINARY)
val g2d: Graphics2D = rotated.createGraphics()
g2d.translate((newWidth - w) / 2, (newHeight - h) / 2)
g2d.rotate(rads, w / 2.0, h / 2.0)
g2d.drawImage(image, 0, 0, null)
g2d.dispose()
return rotated
}

/**
* Converts a BufferedImage (monochrome) to a ZPL string using the ^GFA command.
* This function handles the conversion of pixel data to a compressed hex string.
*
* @param image The BufferedImage to convert.
* @param printDirection Valid values: N (Normal), R (Rotated 90), I (Inverted 180), B (Bottom-up 270)
* @return A ZPL-formatted string ready to be sent to the printer.
*/
private fun convertImageToZpl(image: BufferedImage): String {
private fun convertImageToZpl(image: BufferedImage, printDirection: PrintDirection = PrintDirection.NORMAL): String {
val rotatedImage = rotateImage(image, printDirection.degree)
val zpl = StringBuilder()
zpl.append("^XA\n")
zpl.append("^XA")

val width = image.width
val height = image.height
val width = rotatedImage.width
val height = rotatedImage.height

// ZPL format for a graphical image is ^GFA
val bytesPerRow = (width + 7) / 8
val totalBytes = bytesPerRow * height

zpl.append("^FO0,0^GFA,").append(totalBytes).append(",").append(totalBytes).append(",").append(bytesPerRow).append(",")
println(zpl)

// Extract pixel data and convert to ZPL hex string
for (y in 0 until height) {
val rowBits = BitSet(width)
for (x in 0 until width) {
// Check for a black pixel (0xFF000000 in ARGB)
if (image.getRGB(x, y) == 0xFF000000.toInt()) {
if (rotatedImage.getRGB(x, y) == 0xFF000000.toInt()) {
rowBits.set(x)
}
}


+ 1
- 1
src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt Datei anzeigen

@@ -5,7 +5,7 @@ import com.ffii.fpsms.m18.M18Config
import com.ffii.fpsms.m18.service.*
import com.ffii.fpsms.m18.web.models.M18CommonRequest
import com.ffii.fpsms.modules.common.SettingNames
import com.ffii.fpsms.modules.common.scheduler.SchedulerService
import com.ffii.fpsms.modules.common.scheduler.service.SchedulerService
import com.ffii.fpsms.modules.master.entity.ItemUom
import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.modules.master.entity.ShopRepository


+ 1
- 2
src/main/java/com/ffii/fpsms/modules/common/scheduler/service/SchedulerService.kt Datei anzeigen

@@ -1,4 +1,4 @@
package com.ffii.fpsms.modules.common.scheduler
package com.ffii.fpsms.modules.common.scheduler.service

import com.ffii.core.utils.JwtTokenUtil
import com.ffii.fpsms.m18.service.M18DeliveryOrderService
@@ -14,7 +14,6 @@ import org.slf4j.LoggerFactory
import org.springframework.scheduling.TaskScheduler
import org.springframework.scheduling.support.CronTrigger
import org.springframework.stereotype.Service
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.HashMap


+ 32
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/Printer.kt Datei anzeigen

@@ -0,0 +1,32 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.entity.BaseEntity
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.Id
import jakarta.persistence.Table
import jakarta.validation.constraints.Size

@Entity
@Table(name = "printer")
open class Printer : BaseEntity<Long>() {
@Size(max = 500)
@Column(name = "code", length = 500)
open var code: String? = null

@Size(max = 500)
@Column(name = "name", length = 500)
open var name: String? = null

@Size(max = 500)
@Column(name = "description", length = 500)
open var description: String? = null

@Size(max = 30)
@Column(name = "ip", length = 30)
open var ip: String? = null

@Size(max = 10)
@Column(name = "port", length = 10)
open var port: Int? = null
}

+ 13
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/PrinterRepository.kt Datei anzeigen

@@ -0,0 +1,13 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.master.entity.projections.PrinterCombo
import org.springframework.stereotype.Repository
import java.io.Serializable

@Repository
interface PrinterRepository : AbstractRepository<Printer, Long> {
fun findPrinterComboByDeletedFalse(): List<PrinterCombo>;

fun findByIdAndDeletedFalse(id: Serializable): Printer?;
}

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/WarehouseRepository.kt Datei anzeigen

@@ -1,8 +1,10 @@
package com.ffii.fpsms.modules.master.entity

import com.ffii.core.support.AbstractRepository
import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo
import org.springframework.stereotype.Repository

@Repository
interface WarehouseRepository : AbstractRepository<Warehouse, Long> {
fun findWarehouseComboByDeletedFalse(): List<WarehouseCombo>;
}

+ 16
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/projections/PrinterCombo.kt Datei anzeigen

@@ -0,0 +1,16 @@
package com.ffii.fpsms.modules.master.entity.projections

import org.springframework.beans.factory.annotation.Value

interface PrinterCombo {
val id: Long;
@get:Value("#{target.name}")
val label: String?;
@get:Value("#{target.id}")
val value: String;
val code: String?;
val name: String?;
val description: String?;
val ip: String?;
val port: Int?;
}

+ 11
- 0
src/main/java/com/ffii/fpsms/modules/master/entity/projections/WarehouseCombo.kt Datei anzeigen

@@ -0,0 +1,11 @@
package com.ffii.fpsms.modules.master.entity.projections

import org.springframework.beans.factory.annotation.Value

interface WarehouseCombo {
val id: Long;
@get:Value("#{target.id}")
val value: Long;
@get:Value("#{target.code} - #{target.name}")
val label: String;
}

+ 19
- 0
src/main/java/com/ffii/fpsms/modules/master/service/PrinterService.kt Datei anzeigen

@@ -0,0 +1,19 @@
package com.ffii.fpsms.modules.master.service

import com.ffii.fpsms.modules.master.entity.Printer
import com.ffii.fpsms.modules.master.entity.PrinterRepository
import com.ffii.fpsms.modules.master.entity.projections.PrinterCombo
import org.springframework.stereotype.Service

@Service
open class PrinterService(
val printerRepository: PrinterRepository
) {
open fun findCombo(): List<PrinterCombo> {
return printerRepository.findPrinterComboByDeletedFalse();
}

open fun findById(id: Long): Printer? {
return printerRepository.findByIdAndDeletedFalse(id)
}
}

+ 5
- 0
src/main/java/com/ffii/fpsms/modules/master/service/WarehouseService.kt Datei anzeigen

@@ -5,6 +5,7 @@ import com.ffii.core.support.JdbcDao
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.entity.Warehouse
import com.ffii.fpsms.modules.master.entity.WarehouseRepository
import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo
import com.ffii.fpsms.modules.purchaseOrder.entity.PurchaseOrderLineRepository
import com.ffii.fpsms.modules.stock.entity.InventoryLotRepository
import com.ffii.fpsms.modules.stock.entity.StockInLine
@@ -23,4 +24,8 @@ open class WarehouseService(
open fun getWarehouses(): List<Warehouse> {
return warehouseRepository.findAll().filter { it.deleted == false }
}

open fun findCombo(): List<WarehouseCombo> {
return warehouseRepository.findWarehouseComboByDeletedFalse();
}
}

+ 18
- 0
src/main/java/com/ffii/fpsms/modules/master/web/PrinterController.kt Datei anzeigen

@@ -0,0 +1,18 @@
package com.ffii.fpsms.modules.master.web

import com.ffii.fpsms.modules.master.entity.projections.PrinterCombo
import com.ffii.fpsms.modules.master.service.PrinterService
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RequestMapping("printers")
@RestController
class PrinterController(
private val printerService: PrinterService
) {
@GetMapping("/combo")
fun findCombo(): List<PrinterCombo> {
return printerService.findCombo();
}
}

+ 1
- 3
src/main/java/com/ffii/fpsms/modules/master/web/ProductionScheduleController.kt Datei anzeigen

@@ -2,7 +2,7 @@ package com.ffii.fpsms.modules.master.web

import com.ffii.core.response.RecordsRes
import com.ffii.core.utils.CriteriaArgsBuilder
import com.ffii.fpsms.modules.common.scheduler.SchedulerService
import com.ffii.fpsms.modules.common.scheduler.service.SchedulerService
import com.ffii.fpsms.modules.master.entity.ProductionScheduleRepository
import com.ffii.fpsms.modules.master.entity.projections.DetailedProdScheduleWithLine
import com.ffii.fpsms.modules.master.entity.projections.ProdScheduleInfo
@@ -16,14 +16,12 @@ import com.ffii.fpsms.modules.master.web.models.SearchProdScheduleRequest
import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
import org.springframework.web.bind.annotation.*
import java.time.Duration
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.HashMap
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.math.abs


@RestController


+ 6
- 0
src/main/java/com/ffii/fpsms/modules/master/web/WarehouseController.kt Datei anzeigen

@@ -1,6 +1,7 @@
package com.ffii.fpsms.modules.master.web

import com.ffii.fpsms.modules.master.entity.Warehouse
import com.ffii.fpsms.modules.master.entity.projections.WarehouseCombo
import com.ffii.fpsms.modules.master.service.WarehouseService
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
@@ -15,4 +16,9 @@ class WarehouseController(
fun getWarehouses(): List<Warehouse> {
return warehouseService.getWarehouses()
}

@GetMapping("/combo")
fun findCombo(): List<WarehouseCombo> {
return warehouseService.findCombo()
}
}

+ 3
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/EscalationLogInfo.kt Datei anzeigen

@@ -28,6 +28,9 @@ interface EscalationLogInfo {
@get:Value("#{target.stockInLine?.dnDate}")
val dnDate: LocalDateTime?

@get:Value("#{target.stockInLine?.item?.code} - #{target.stockInLine?.item?.name}")
val item: String?

@get:Value("#{target.stockInLine?.item?.code}")
val itemCode: String?



+ 6
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLot.kt Datei anzeigen

@@ -1,6 +1,8 @@
package com.ffii.fpsms.modules.stock.entity

import com.fasterxml.jackson.annotation.JsonBackReference
import com.fasterxml.jackson.annotation.JsonIdentityInfo
import com.fasterxml.jackson.annotation.ObjectIdGenerators
import com.ffii.core.entity.BaseEntity
import com.ffii.fpsms.modules.master.entity.Items
import jakarta.persistence.*
@@ -11,6 +13,7 @@ import org.hibernate.type.SqlTypes
import java.time.LocalDate
import java.time.LocalDateTime

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator::class, property = "id")
@Entity
@Table(name = "inventory_lot")
open class InventoryLot : BaseEntity<Long>() {
@@ -38,6 +41,9 @@ open class InventoryLot : BaseEntity<Long>() {
@Column(name = "lotNo")
open var lotNo: String? = null

@OneToMany(mappedBy = "inventoryLot", cascade = [CascadeType.ALL], orphanRemoval = true)
open var inventoryLotLines: MutableList<InventoryLotLine>? = null

// @JdbcTypeCode(SqlTypes.JSON)
// @Column(name = "qrCodeJson")
// open var qrCodeJson: MutableMap<String, Any>? = null

+ 2
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/InventoryLotLineRepository.kt Datei anzeigen

@@ -31,4 +31,6 @@ interface InventoryLotLineRepository : AbstractRepository<InventoryLotLine, Long
fun findCurrentInventoryByItems(items: List<Serializable>): List<CurrentInventoryItemInfo>

fun findAllByIdIn(ids: List<Serializable>): List<InventoryLotLine>

fun findAllByInventoryLotId(id: Serializable): List<InventoryLotLine>
}

+ 2
- 2
src/main/java/com/ffii/fpsms/modules/stock/entity/projection/InventoryInfo.kt Datei anzeigen

@@ -28,13 +28,13 @@ interface InventoryInfo{
val availableQty: BigDecimal?
@get:Value("#{target.item.itemUoms.^[stockUnit == true && deleted == false]?.uom.code}")
val uomCode: String?
@get:Value("#{target.item.itemUoms.^[stockUnit == true && deleted == false]?.uom.udfudesc}")
@get:Value("#{target.item.itemUoms.^[stockUnit == true && deleted == false]?.uom?.udfudesc}")
val uomUdfudesc: String?
// @get:Value("#{target.qty * target.uom.gramPerSmallestUnit}")
// val germPerSmallestUnit: BigDecimal?
@get:Value("#{(target.onHandQty - target.onHoldQty - target.unavailableQty)}")
val qtyPerSmallestUnit: BigDecimal?
@get:Value("#{target.item.itemUoms.^[baseUnit == true && deleted == false]?.uom.udfudesc}")
@get:Value("#{target.item.itemUoms.^[baseUnit == true && deleted == false]?.uom?.udfudesc}")
val baseUom: String?
// @get:Value("#{target.qty * (target.uom.unit4 != '' ? target.uom.unit4Qty " +
// ": target.uom.unit3 != '' ? target.uom.unit3Qty " +


+ 19
- 3
src/main/java/com/ffii/fpsms/modules/stock/entity/projection/StockInLineInfo.kt Datei anzeigen

@@ -1,9 +1,7 @@
package com.ffii.fpsms.modules.stock.entity.projection

import com.ffii.fpsms.modules.master.entity.Items
import com.ffii.fpsms.modules.master.entity.ItemsRepository
import com.ffii.fpsms.modules.master.entity.UomConversion
import com.ffii.fpsms.modules.stock.enums.EscalationLogStatus
import com.ffii.fpsms.modules.stock.entity.InventoryLot
import org.springframework.beans.factory.annotation.Value
import java.math.BigDecimal
import java.time.LocalDate
@@ -48,4 +46,22 @@ interface StockInLineInfo {
val dnDate: LocalDateTime?
@get:Value("#{target.escalationLog.^[status.value == 'pending']?.handler?.id}")
val handlerId: Long?
@get:Value("#{target.inventoryLot?.inventoryLotLines ?: new java.util.ArrayList()}")
val putAwayLines: List<PutAwayLineForSil>;
}

interface PutAwayLineForSil {
val id: Long?;
@get:Value("#{target.inQty " +
"* ((target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[stockUnit == true && deleted == false]?.ratioD))" +
"/ ((target.inventoryLot.item.itemUoms.^[purchaseUnit == true && deleted == false]?.ratioN / target.inventoryLot.item.itemUoms.^[purchaseUnit == true && deleted == false]?.ratioD))}")
val qty: BigDecimal?;
@get:Value("#{target.warehouse?.code} - #{target.warehouse?.name}")
val warehouse: String?;
@get:Value("#{target.warehouse?.id}")
val warehouseId: Long?;
@get:Value("#{target.warehouse?.code}")
val warehouseCode: String?;
@get:Value("#{target.warehouse?.name}")
val warehouseName: String?;
}

+ 104
- 26
src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt Datei anzeigen

@@ -22,20 +22,25 @@ import java.math.BigDecimal
import java.time.LocalDate
import java.time.LocalDateTime
import com.ffii.core.utils.PdfUtils;
import com.ffii.core.utils.ZebraPrinterUtil
import com.ffii.fpsms.modules.master.entity.ItemUomRespository
import com.ffii.fpsms.modules.master.entity.WarehouseRepository
import com.ffii.fpsms.modules.master.service.PrinterService
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.stock.entity.enum.InventoryLotLineStatus
import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo
import com.ffii.fpsms.modules.stock.enums.EscalationLogStatus
import com.ffii.fpsms.modules.stock.web.model.*
import com.ffii.fpsms.modules.stock.enums.EscalationLogStatus
import java.io.FileNotFoundException
import java.time.format.DateTimeFormatter
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString
import net.sf.jasperreports.engine.JasperExportManager
import net.sf.jasperreports.engine.JasperPrint
import java.io.File
import kotlin.math.max

@Serializable
@@ -58,6 +63,7 @@ open class StockInLineService(
private val itemRepository: ItemsRepository,
private val warehouseRepository: WarehouseRepository,
private val itemUomRespository: ItemUomRespository,
private val printerService: PrinterService,
): AbstractBaseEntityService<StockInLine, Long, StockInLineRepository>(jdbcDao, stockInLineRepository) {

open fun getStockInLineInfo(stockInLineId: Long): StockInLineInfo {
@@ -139,26 +145,53 @@ open class StockInLineService(

@Throws(IOException::class)
@Transactional
fun saveInventoryLotLineWhenStockIn(request: SaveStockInLineRequest, stockInLine: StockInLine): InventoryLotLine {
val inventoryLotLine = InventoryLotLine()
val warehouse = warehouseRepository.findById(request.warehouseId!!).orElseThrow()
val stockItemUom = itemUomRespository.findBaseUnitByItemIdAndStockUnitIsTrueAndDeletedIsFalse(
itemId = request.itemId
)
val convertedBaseQty = if (stockItemUom != null) {
(request.acceptQty?:request.acceptedQty) * stockItemUom.ratioN!! / stockItemUom.ratioD!!
} else {
(request.acceptQty?:request.acceptedQty)
}
inventoryLotLine.apply {
this.inventoryLot = stockInLine.inventoryLot
this.warehouse = warehouse
this.inQty = convertedBaseQty
this.status = InventoryLotLineStatus.AVAILABLE
this.stockUom = stockItemUom
fun saveInventoryLotLineWhenStockIn(request: SaveStockInLineRequest, stockInLine: StockInLine): List<InventoryLotLine> {
val response = request.inventoryLotLines?.let { lines ->
val saveLines = mutableListOf<InventoryLotLine>();
lines.forEach { line ->
val inventoryLotLine = InventoryLotLine()
val warehouse = warehouseRepository.findById(line.warehouseId!!).orElseThrow()
val stockItemUom = itemUomRespository.findBaseUnitByItemIdAndStockUnitIsTrueAndDeletedIsFalse(
itemId = request.itemId
)
val purchaseItemUom = itemUomRespository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(request.itemId)
val convertedBaseQty = if (stockItemUom != null && purchaseItemUom != null) {
(line.qty) * (purchaseItemUom.ratioN!! / purchaseItemUom.ratioD!!) / (stockItemUom.ratioN!! / stockItemUom.ratioD!!)
} else {
(line.qty)
}
inventoryLotLine.apply {
this.inventoryLot = stockInLine.inventoryLot
this.warehouse = warehouse
this.inQty = convertedBaseQty
this.status = InventoryLotLineStatus.AVAILABLE
this.stockUom = stockItemUom
}
saveLines.add(inventoryLotLine)
}
inventoryLotLineRepository.saveAll(saveLines)
}
val savedInventoryLotLine = inventoryLotLineRepository.saveAndFlush(inventoryLotLine)
return savedInventoryLotLine

return response ?: emptyList();
// val inventoryLotLine = InventoryLotLine()
// val warehouse = warehouseRepository.findById(request.warehouseId!!).orElseThrow()
// val stockItemUom = itemUomRespository.findBaseUnitByItemIdAndStockUnitIsTrueAndDeletedIsFalse(
// itemId = request.itemId
// )
// val convertedBaseQty = if (stockItemUom != null) {
// (request.acceptQty?:request.acceptedQty) * stockItemUom.ratioN!! / stockItemUom.ratioD!!
// } else {
// (request.acceptQty?:request.acceptedQty)
// }
// inventoryLotLine.apply {
// this.inventoryLot = stockInLine.inventoryLot
// this.warehouse = warehouse
// this.inQty = convertedBaseQty
// this.status = InventoryLotLineStatus.AVAILABLE
// this.stockUom = stockItemUom
// }
// val savedInventoryLotLine = inventoryLotLineRepository.saveAndFlush(inventoryLotLine)
// return savedInventoryLotLine
}

@Throws(IOException::class)
@@ -221,7 +254,7 @@ open class StockInLineService(

@Throws(IOException::class)
@Transactional
fun updatePurchaseOrderStatus(request: SaveStockInLineRequest) {
open fun updatePurchaseOrderStatus(request: SaveStockInLineRequest) {
if (request.status == StockInLineStatus.COMPLETE.status) {
val unfinishedLines = polRepository
.findAllByPurchaseOrderIdAndStatusNotAndDeletedIsFalse(purchaseOrderId = request.purchaseOrderId, status = PurchaseOrderLineStatus.COMPLETED)
@@ -320,10 +353,23 @@ open class StockInLineService(
// Putaway
var savedInventoryLotLine: InventoryLotLine? = null

savedInventoryLotLine = saveInventoryLotLineWhenStockIn(request = request, stockInLine = stockInLine)
stockInLine.apply {
this.status = StockInLineStatus.COMPLETE.status
this.inventoryLotLine = savedInventoryLotLine
// savedInventoryLotLine = saveInventoryLotLineWhenStockIn(request = request, stockInLine = stockInLine)
val savedInventoryLotLines = saveInventoryLotLineWhenStockIn(request = request, stockInLine = stockInLine)
val inventoryLotLines = stockInLine.inventoryLot?.let { it.id?.let { _id -> inventoryLotLineRepository.findAllByInventoryLotId(_id) } } ?: listOf()

val purchaseItemUom = itemUomRespository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(request.itemId)
val stockItemUom = itemUomRespository.findByItemIdAndStockUnitIsTrueAndDeletedIsFalse(request.itemId)
val ratio = if (stockItemUom != null && purchaseItemUom != null) {
(purchaseItemUom.ratioN!! / purchaseItemUom.ratioD!!) / (stockItemUom.ratioN!! / stockItemUom.ratioD!!)
} else {
BigDecimal.ONE
}

if (inventoryLotLines.sumOf { it.inQty ?: BigDecimal.ZERO } >= request.acceptQty?.times(ratio)) {
stockInLine.apply {
this.status = StockInLineStatus.COMPLETE.status
// this.inventoryLotLine = savedInventoryLotLine
}
}
} else if (request.status == StockInLineStatus.PENDING.status || request.status == StockInLineStatus.ESCALATED.status) {
// QC
@@ -481,7 +527,7 @@ open class StockInLineService(
field["uom"] = info.uom.code.toString()
field["productionDate"] = info.productionDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: ""
field["expiryDate"] = info.expiryDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) ?: ""
field["lotNo"] = info.lotNo!!
field["lotNo"] = info.lotNo ?: ""
field["supplier"] = info.supplier!!
val image = QrCodeUtil.generateQRCodeImage(qrCodeContent)
field["qrCode"] = image
@@ -495,4 +541,36 @@ open class StockInLineService(
"fileName" to qrCodeInfo[0].poCode
);
}

@Transactional
open fun printQrCode(request: PrintQrCodeForSilRequest) {
val printer = printerService.findById(request.printerId) ?: throw NoSuchElementException("No such printer");

val pdf = exportStockInLineQrcode(
ExportQrCodeRequest(
stockInLineIds = listOf(request.stockInLineId)
)
)

val jasperPrint = pdf["report"] as JasperPrint

// 1. Create a temporary file to save the PDF.
val tempPdfFile = File.createTempFile("print_job_", ".pdf")

try {
// 2. Export the JasperPrint to the temporary PDF file.
JasperExportManager.exportReportToPdfFile(jasperPrint, tempPdfFile.absolutePath)

// 3. Call the utility function with the temporary file.
val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty
printer.ip?.let { ip -> printer.port?.let { port ->
ZebraPrinterUtil.printPdfToZebra(tempPdfFile, ip, port, printQty,
ZebraPrinterUtil.PrintDirection.ROTATED
)
} }
} finally {
// 4. Ensure the temporary file is deleted after the print job is sent.
tempPdfFile.delete()
}
}
}

+ 6
- 1
src/main/java/com/ffii/fpsms/modules/stock/web/StockInLineController.kt Datei anzeigen

@@ -6,12 +6,14 @@ import com.ffii.fpsms.modules.stock.entity.StockInLine
import com.ffii.fpsms.modules.stock.entity.projection.StockInLineInfo
import com.ffii.fpsms.modules.stock.service.StockInLineService
import com.ffii.fpsms.modules.stock.web.model.ExportQrCodeRequest
import com.ffii.fpsms.modules.stock.web.model.PrintQrCodeForSilRequest
import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest
import jakarta.servlet.http.HttpServletResponse
import jakarta.validation.Valid
import net.sf.jasperreports.engine.JasperExportManager
import net.sf.jasperreports.engine.JasperPrint
import org.springframework.context.NoSuchMessageException
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import java.io.OutputStream
import java.io.UnsupportedEncodingException
@@ -53,5 +55,8 @@ class StockInLineController(
out.write(JasperExportManager.exportReportToPdf(jasperPrint));
}


@GetMapping("/printQrCode")
fun printQrCode(@ModelAttribute request: PrintQrCodeForSilRequest) {
stockInLineService.printQrCode(request)
}
}

+ 8
- 0
src/main/java/com/ffii/fpsms/modules/stock/web/model/PrintQrCodeRequest.kt Datei anzeigen

@@ -0,0 +1,8 @@
package com.ffii.fpsms.modules.stock.web.model

// Stock in line
data class PrintQrCodeForSilRequest(
val stockInLineId: Long,
val printerId: Long,
val printQty: Int?,
)

+ 7
- 1
src/main/java/com/ffii/fpsms/modules/stock/web/model/SaveStockInRequest.kt Datei anzeigen

@@ -56,5 +56,11 @@ data class SaveStockInLineRequest(
var qcResult: List<SaveQcResultRequest>?,
var escalationLog: SaveEscalationLogRequest?,
var warehouseId: Long?,
var rejectQty: BigDecimal?
var rejectQty: BigDecimal?,
var inventoryLotLines: List<SaveInventoryLotLineForSil>?
)

data class SaveInventoryLotLineForSil (
val qty: BigDecimal,
val warehouseId: Long?
)

+ 19
- 0
src/main/resources/db/changelog/changes/20250825_01_cyril/02_create_printer.sql Datei anzeigen

@@ -0,0 +1,19 @@
-- liquibase formatted sql
-- changeset cyril:create_printer

CREATE TABLE `printer`
(
`id` INT NOT NULL AUTO_INCREMENT,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`createdBy` VARCHAR(30) NULL DEFAULT NULL,
`version` INT NOT NULL DEFAULT '0',
`modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modifiedBy` VARCHAR(30) NULL DEFAULT NULL,
`deleted` TINYINT(1) NOT NULL DEFAULT '0',
`code` VARCHAR(500) NULL,
`name` VARCHAR(500) NULL,
`description` VARCHAR(500) NULL,
`ip` VARCHAR(30) NULL,
`port` INT(10) NULL,
PRIMARY KEY (`id`)
);

Laden…
Abbrechen
Speichern