| @@ -0,0 +1 @@ | |||||
| kotlin.daemon.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m | |||||
| @@ -25,9 +25,16 @@ class BrotherPrinterUtil { | |||||
| fun printToBrother(pdfFile: File, printerIp: String, printerPort: Int = 9100, printQty: Int = 1) { | fun printToBrother(pdfFile: File, printerIp: String, printerPort: Int = 9100, printQty: Int = 1) { | ||||
| if (!pdfFile.exists()) throw IllegalArgumentException("File not found.") | if (!pdfFile.exists()) throw IllegalArgumentException("File not found.") | ||||
| println("DEBUG: PDF file size: ${pdfFile.length()} bytes") | |||||
| PDDocument.load(pdfFile).use { document -> | PDDocument.load(pdfFile).use { document -> | ||||
| val renderer = PDFRenderer(document) | val renderer = PDFRenderer(document) | ||||
| val totalPages = document.numberOfPages | |||||
| repeat(printQty) { copyIndex -> | |||||
| println("DEBUG: Printing copy ${copyIndex + 1} of $printQty") | |||||
| } | |||||
| Socket(printerIp, printerPort).use { socket -> | Socket(printerIp, printerPort).use { socket -> | ||||
| val os = socket.getOutputStream() | val os = socket.getOutputStream() | ||||
| @@ -49,6 +56,7 @@ class BrotherPrinterUtil { | |||||
| // 4. End Job | // 4. End Job | ||||
| os.write("\u001B%-12345X".toByteArray(Charsets.US_ASCII)) | os.write("\u001B%-12345X".toByteArray(Charsets.US_ASCII)) | ||||
| os.flush() | os.flush() | ||||
| println("DEBUG: Print job sent to printer") | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -74,7 +74,7 @@ import com.ffii.fpsms.modules.pickOrder.entity.PickExecutionIssueRepository // | |||||
| import com.ffii.core.utils.CanonPrinterUtil | import com.ffii.core.utils.CanonPrinterUtil | ||||
| import com.ffii.fpsms.modules.master.entity.ItemsRepository | import com.ffii.fpsms.modules.master.entity.ItemsRepository | ||||
| import kotlin.collections.emptyMap | import kotlin.collections.emptyMap | ||||
| import com.ffii.fpsms.modules.master.print.A4PrintDriverRegistry | |||||
| import com.ffii.core.response.RecordsRes | import com.ffii.core.response.RecordsRes | ||||
| import org.springframework.data.domain.PageRequest | import org.springframework.data.domain.PageRequest | ||||
| import org.springframework.data.domain.Page | import org.springframework.data.domain.Page | ||||
| @@ -1303,24 +1303,10 @@ open class DeliveryOrderService( | |||||
| val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty | val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty | ||||
| val duplexMode = if (CanonPrinterUtil.isLandscape(tempPdfFile)) { | |||||
| CanonPrinterUtil.DuplexMode.DUPLEX_SHORT_EDGE // Landscape: flip on short edge | |||||
| } else { | |||||
| CanonPrinterUtil.DuplexMode.DUPLEX_LONG_EDGE // Portrait: flip on long edge | |||||
| } | |||||
| println("DEBUG: PDF orientation detected - Landscape: ${CanonPrinterUtil.isLandscape(tempPdfFile)}, Duplex mode: $duplexMode") | |||||
| printer.ip?.let { ip -> | printer.ip?.let { ip -> | ||||
| printer.port?.let { port -> | |||||
| CanonPrinterUtil.printPdfToCanon( | |||||
| tempPdfFile, | |||||
| ip, | |||||
| port, | |||||
| printQty, | |||||
| duplexMode | |||||
| ) | |||||
| } | |||||
| val port = printer.port ?: 9100 | |||||
| val driver = A4PrintDriverRegistry.getDriver(printer.brand) | |||||
| driver.print(tempPdfFile, ip, port, printQty) | |||||
| } | } | ||||
| } finally { | } finally { | ||||
| //tempPdfFile.delete() | //tempPdfFile.delete() | ||||
| @@ -72,6 +72,7 @@ import com.ffii.fpsms.modules.master.service.ItemUomService | |||||
| import com.ffii.fpsms.modules.master.web.models.ConvertUomByItemRequest | import com.ffii.fpsms.modules.master.web.models.ConvertUomByItemRequest | ||||
| import com.ffii.fpsms.modules.stock.service.StockInLineService | import com.ffii.fpsms.modules.stock.service.StockInLineService | ||||
| import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest | import com.ffii.fpsms.modules.stock.web.model.SaveStockInLineRequest | ||||
| import com.ffii.fpsms.modules.master.print.A4PrintDriverRegistry | |||||
| @Service | @Service | ||||
| open class JobOrderService( | open class JobOrderService( | ||||
| val jobOrderRepository: JobOrderRepository, | val jobOrderRepository: JobOrderRepository, | ||||
| @@ -95,6 +96,7 @@ open class JobOrderService( | |||||
| val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, | val jobOrderBomMaterialRepository: JobOrderBomMaterialRepository, | ||||
| val bomMaterialRepository: BomMaterialRepository, | val bomMaterialRepository: BomMaterialRepository, | ||||
| val itemUomService: ItemUomService | val itemUomService: ItemUomService | ||||
| ) { | ) { | ||||
| open fun allJobOrdersByPage(request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfo> { | open fun allJobOrdersByPage(request: SearchJobOrderInfoRequest): RecordsRes<JobOrderInfo> { | ||||
| @@ -783,25 +785,10 @@ open class JobOrderService( | |||||
| val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty | val printQty = if (request.printQty == null || request.printQty <= 0) 1 else request.printQty | ||||
| // Auto-detect orientation and set duplex mode accordingly | |||||
| val duplexMode = if (CanonPrinterUtil.isLandscape(tempPdfFile)) { | |||||
| CanonPrinterUtil.DuplexMode.DUPLEX_SHORT_EDGE // Landscape: flip on short edge | |||||
| } else { | |||||
| CanonPrinterUtil.DuplexMode.DUPLEX_LONG_EDGE // Portrait: flip on long edge | |||||
| } | |||||
| println("DEBUG: PDF orientation detected - Landscape: ${CanonPrinterUtil.isLandscape(tempPdfFile)}, Duplex mode: $duplexMode") | |||||
| printer.ip?.let { ip -> | printer.ip?.let { ip -> | ||||
| printer.port?.let { port -> | |||||
| CanonPrinterUtil.printPdfToCanon( | |||||
| tempPdfFile, | |||||
| ip, | |||||
| port, | |||||
| printQty, | |||||
| duplexMode | |||||
| ) | |||||
| } | |||||
| val port = printer.port ?: 9100 | |||||
| val driver = A4PrintDriverRegistry.getDriver(printer.brand) | |||||
| driver.print(tempPdfFile, ip, port, printQty) | |||||
| } | } | ||||
| } finally { | } finally { | ||||
| //tempPdfFile.delete() | //tempPdfFile.delete() | ||||
| @@ -22,6 +22,10 @@ open class Printer : BaseEntity<Long>() { | |||||
| @Column(name = "type", nullable = true, length = 255) | @Column(name = "type", nullable = true, length = 255) | ||||
| open var type: String? = null | open var type: String? = null | ||||
| @Size(max = 255) | |||||
| @Column(name = "brand", nullable = true, length = 255) | |||||
| open var brand: String? = null | |||||
| @Size(max = 500) | @Size(max = 500) | ||||
| @Column(name = "description", length = 500) | @Column(name = "description", length = 500) | ||||
| open var description: String? = null | open var description: String? = null | ||||
| @@ -14,4 +14,5 @@ interface PrinterCombo { | |||||
| val description: String?; | val description: String?; | ||||
| val ip: String?; | val ip: String?; | ||||
| val port: Int?; | val port: Int?; | ||||
| val brand: String?; | |||||
| } | } | ||||
| @@ -0,0 +1,11 @@ | |||||
| package com.ffii.fpsms.modules.master.print | |||||
| import java.io.File | |||||
| /** | |||||
| * Abstraction for A4 (document) printing. | |||||
| * Implementations send PDF to Canon, Brother, etc. via their protocol. | |||||
| */ | |||||
| interface A4PrintDriver { | |||||
| fun print(pdfFile: File, ip: String, port: Int, printQty: Int) | |||||
| } | |||||
| @@ -0,0 +1,18 @@ | |||||
| package com.ffii.fpsms.modules.master.print | |||||
| object A4PrintDriverRegistry { | |||||
| private val drivers = mapOf( | |||||
| "Canon" to CanonA4Driver(), | |||||
| "Brother" to BrotherA4Driver() | |||||
| ) | |||||
| private val defaultDriver = CanonA4Driver() | |||||
| /** | |||||
| * Returns the A4 driver for the given brand. | |||||
| * Null/blank/unknown brand => Canon (backward compatible). | |||||
| */ | |||||
| fun getDriver(brand: String?): A4PrintDriver { | |||||
| if (brand.isNullOrBlank()) return defaultDriver | |||||
| return drivers[brand.trim()] ?: defaultDriver | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,10 @@ | |||||
| package com.ffii.fpsms.modules.master.print | |||||
| import com.ffii.core.utils.BrotherPrinterUtil | |||||
| import java.io.File | |||||
| class BrotherA4Driver : A4PrintDriver { | |||||
| override fun print(pdfFile: File, ip: String, port: Int, printQty: Int) { | |||||
| BrotherPrinterUtil.printToBrother(pdfFile, ip, port, printQty) | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,15 @@ | |||||
| package com.ffii.fpsms.modules.master.print | |||||
| import com.ffii.core.utils.CanonPrinterUtil | |||||
| import java.io.File | |||||
| class CanonA4Driver : A4PrintDriver { | |||||
| override fun print(pdfFile: File, ip: String, port: Int, printQty: Int) { | |||||
| val duplexMode = if (CanonPrinterUtil.isLandscape(pdfFile)) { | |||||
| CanonPrinterUtil.DuplexMode.DUPLEX_SHORT_EDGE | |||||
| } else { | |||||
| CanonPrinterUtil.DuplexMode.DUPLEX_LONG_EDGE | |||||
| } | |||||
| CanonPrinterUtil.printPdfToCanon(pdfFile, ip, port, printQty, duplexMode) | |||||
| } | |||||
| } | |||||
| @@ -40,6 +40,7 @@ open class PrinterService( | |||||
| name = request.name | name = request.name | ||||
| code = request.code | code = request.code | ||||
| type = request.type | type = request.type | ||||
| brand = if (request.type == "Label") "Zebra" else request.brand | |||||
| description = request.description | description = request.description | ||||
| ip = request.ip | ip = request.ip | ||||
| port = request.port | port = request.port | ||||
| @@ -74,6 +75,18 @@ open class PrinterService( | |||||
| request.name?.let { printer.name = it } | request.name?.let { printer.name = it } | ||||
| request.code?.let { printer.code = it } | request.code?.let { printer.code = it } | ||||
| request.type?.let { printer.type = it } | request.type?.let { printer.type = it } | ||||
| request.type?.let { newType -> | |||||
| printer.type = newType | |||||
| if (newType == "Label") { | |||||
| printer.brand = "Zebra" | |||||
| } | |||||
| } | |||||
| request.brand?.let { newBrand -> | |||||
| if (printer.type != "Label") { | |||||
| printer.brand = newBrand | |||||
| } | |||||
| } | |||||
| request.description?.let { printer.description = it } | request.description?.let { printer.description = it } | ||||
| request.ip?.let { printer.ip = it } | request.ip?.let { printer.ip = it } | ||||
| request.port?.let { printer.port = it } | request.port?.let { printer.port = it } | ||||
| @@ -65,7 +65,8 @@ data class PrinterCreateRequest( | |||||
| val description: String? = null, | val description: String? = null, | ||||
| val ip: String? = null, | val ip: String? = null, | ||||
| val port: Int? = null, | val port: Int? = null, | ||||
| val dpi: Int? = null | |||||
| val dpi: Int? = null, | |||||
| val brand: String? = null | |||||
| ) | ) | ||||
| data class PrinterUpdateRequest( | data class PrinterUpdateRequest( | ||||
| @@ -75,5 +76,6 @@ data class PrinterUpdateRequest( | |||||
| val description: String? = null, | val description: String? = null, | ||||
| val ip: String? = null, | val ip: String? = null, | ||||
| val port: Int? = null, | val port: Int? = null, | ||||
| val dpi: Int? = null | |||||
| val dpi: Int? = null, | |||||
| val brand: String? = null | |||||
| ) | ) | ||||
| @@ -0,0 +1,5 @@ | |||||
| -- liquibase formatted sql | |||||
| -- changeset KelvinY:add_brand_to_printer | |||||
| ALTER TABLE `fpsmsdb`.`printer` | |||||
| ADD COLUMN `brand` VARCHAR(255) NULL; | |||||