소스 검색

Merge branch 'master' of https://git.2fi-solutions.com/jason/FPSMS-backend

reset-do-picking-order
Tommy\2Fi-Staff 6 일 전
부모
커밋
c0c50a6832
5개의 변경된 파일54개의 추가작업 그리고 42개의 파일을 삭제
  1. +33
    -39
      src/main/java/com/ffii/core/utils/BrotherPrinterUtil.kt
  2. +1
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt
  3. +2
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt
  4. +4
    -0
      src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt
  5. +14
    -3
      src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt

+ 33
- 39
src/main/java/com/ffii/core/utils/BrotherPrinterUtil.kt 파일 보기

@@ -5,58 +5,51 @@ import org.apache.pdfbox.rendering.PDFRenderer
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.OutputStream
import java.net.Socket
import javax.imageio.ImageIO
import org.slf4j.LoggerFactory

class BrotherPrinterUtil {

companion object {
private const val SOCKET_TIMEOUT_MS = 120_000 // 2 min for large jobs; Brother may be slow to accept
private const val BROTHER_DPI = 200 // Lower DPI = smaller job; some Brother models buffer better at 200

private val log = LoggerFactory.getLogger(BrotherPrinterUtil::class.java)

/**
* //Usage
* BrotherPrinterUtil.printToBrother(
pdfFile = File("document.pdf"),
printerIp = "192.168.1.50",
printQty = 1
)

* Sends a PDF to a Brother DCP-1610W by rendering it to a PCL-compatible format.
* Sends a PDF to a Brother (e.g. DCP-1610W) by rendering to PCL raster over port 9100.
* Uses socket timeout and flush-per-page so the printer has time to accept data.
*/
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: ${pdfFile.absolutePath}")

println("DEBUG: PDF file size: ${pdfFile.length()} bytes")
log.info("Brother print: file=${pdfFile.name} size=${pdfFile.length()} ip=$printerIp port=$printerPort copies=$printQty")

PDDocument.load(pdfFile).use { 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.soTimeout = SOCKET_TIMEOUT_MS
val os = socket.getOutputStream()
// 1. Start PJL Job
// 1. PJL header (optional; some Brother prefer minimal PJL)
os.write(getPjlHeader(printQty))
os.flush()

// 2. Render each page as a 300 DPI image
for (pageIndex in 0 until document.numberOfPages) {
val image = renderer.renderImageWithDPI(pageIndex, 300f, org.apache.pdfbox.rendering.ImageType.BINARY)
// 3. Convert Image to PCL Raster Data
val pclData = convertImageToPcl(image)
os.write(pclData)
// Form Feed (Move to next page)
os.write("\u000C".toByteArray())
repeat(printQty) { copyIndex ->
for (pageIndex in 0 until totalPages) {
val image = renderer.renderImageWithDPI(pageIndex, BROTHER_DPI.toFloat(), org.apache.pdfbox.rendering.ImageType.BINARY)
val pclData = convertImageToPcl(image, BROTHER_DPI)
os.write(pclData)
os.write("\u000C".toByteArray(Charsets.US_ASCII)) // Form feed
os.flush() // Flush per page so Brother receives incrementally
}
}

// 4. End Job
os.write("\u001B%-12345X".toByteArray(Charsets.US_ASCII))
os.flush()
println("DEBUG: Print job sent to printer")
log.info("Brother print job sent successfully")
}
}
}
@@ -64,24 +57,25 @@ class BrotherPrinterUtil {
private fun getPjlHeader(qty: Int): ByteArray {
val pjl = StringBuilder()
pjl.append("\u001B%-12345X@PJL\r\n")
pjl.append("@PJL SET COPIES=$qty\r\n")
pjl.append("@PJL ENTER LANGUAGE=PCL\r\n") // Tell printer to expect PCL graphics, not PDF
pjl.append("@PJL SET COPIES=1\r\n") // We send copies in app; some Brother ignore COPIES or mis-handle it
pjl.append("@PJL ENTER LANGUAGE=PCL\r\n")
return pjl.toString().toByteArray(Charsets.US_ASCII)
}

/**
* Converts a BufferedImage into PCL Level 3/5 Raster Graphics.
* This is the "magic" that allows a budget printer to print a PDF.
* Converts a BufferedImage into PCL raster graphics for Brother.
* Uses given DPI (e.g. 200) to keep job size smaller for printers with limited buffer.
*/
private fun convertImageToPcl(image: BufferedImage): ByteArray {
private fun convertImageToPcl(image: BufferedImage, dpi: Int): ByteArray {
val out = ByteArrayOutputStream()
val width = image.width
val height = image.height

// PCL: Start Graphics at 300 DPI
out.write("\u001B*t300R".toByteArray())
// PCL: Start Raster Graphics
out.write("\u001B*r1A".toByteArray())
// PCL reset (Brother often needs clean state)
out.write("\u001BE".toByteArray(Charsets.US_ASCII)) // Esc E = reset
// PCL: Set resolution and start raster
out.write("\u001B*t${dpi}R".toByteArray(Charsets.US_ASCII))
out.write("\u001B*r1A".toByteArray(Charsets.US_ASCII))

for (y in 0 until height) {
val rowBytes = (width + 7) / 8


+ 1
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/JobOrderRepository.kt 파일 보기

@@ -46,6 +46,7 @@ interface JobOrderRepository : AbstractRepository<JobOrder, Long> {
b.code as itemCode,
b.name,
jo.reqQty,
jo.bomId,
b.outputQtyUom as unit,
uc2.udfudesc as uom,
COALESCE(uc2.udfShortDesc, uc2.udfudesc) as shortUom,


+ 2
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/entity/projections/JobOrderInfo.kt 파일 보기

@@ -76,6 +76,7 @@ interface JobOrderDetailWithJsonString {
val code: String?;
val itemCode: String?;
val name: String?;
val bomId: Long?;
val itemId: Long?;
val reqQty: BigDecimal?;
val uom: String?;
@@ -115,6 +116,7 @@ data class JobOrderDetail(
val code: String?,
val itemCode: String?,
val name: String?,
val bomId: Long?,
val reqQty: BigDecimal?,
val uom: String?,
val shortUom: String?,


+ 4
- 0
src/main/java/com/ffii/fpsms/modules/jobOrder/service/JobOrderService.kt 파일 보기

@@ -334,6 +334,7 @@ open class JobOrderService(
name = sqlResult.name,
reqQty = sqlResult.reqQty,
uom = sqlResult.uom,
bomId = sqlResult.bomId,
pickLines = jsonResult,
status = sqlResult.status,
shortUom = sqlResult.shortUom
@@ -352,6 +353,7 @@ open class JobOrderService(
name = sqlResult.name,
reqQty = sqlResult.reqQty,
uom = sqlResult.uom,
bomId = sqlResult.bomId,
pickLines = jsonResult,
status = sqlResult.status,
shortUom = sqlResult.shortUom
@@ -371,6 +373,7 @@ open class JobOrderService(
code = sqlResult.code,
itemCode = sqlResult.itemCode,
name = sqlResult.name,
bomId = sqlResult.bomId,
reqQty = sqlResult.reqQty,
uom = sqlResult.uom,
pickLines = jsonResult,
@@ -390,6 +393,7 @@ open class JobOrderService(
code = sqlResult.code,
itemCode = sqlResult.itemCode,
name = sqlResult.name,
bomId = sqlResult.bomId,
reqQty = sqlResult.reqQty,
uom = sqlResult.uom,
pickLines = jsonResult,


+ 14
- 3
src/main/java/com/ffii/fpsms/modules/stock/service/StockInLineService.kt 파일 보기

@@ -298,7 +298,7 @@ open class StockInLineService(
)
val purchaseItemUom = itemUomRepository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(request.itemId)
// PO-origin: frontend sends qty in stock; non-PO: treat as purchase and convert to stock
val convertedBaseQty = if (stockInLine.purchaseOrderLine != null) {
val convertedBaseQty = if (stockInLine.purchaseOrderLine != null || stockInLine.jobOrder != null) {
line.qty
} else if (request.stockTakeLineId == null && stockItemUom != null && purchaseItemUom != null) {
(line.qty) * (purchaseItemUom.ratioN!! / purchaseItemUom.ratioD!!) / (stockItemUom.ratioN!! / stockItemUom.ratioD!!)
@@ -680,6 +680,17 @@ open class StockInLineService(
} else {
requestQty ?: this.acceptedQty
}
} else if (request.qcAccept == true && this.status == StockInLineStatus.ESCALATED.status) {
// Case: line was escalated (QC decision 3), handler resolves with decision 1 (accept).
// Use 揀收數量 (acceptQty) for put away instead of keeping original received qty.
val requestQty = request.acceptQty ?: request.acceptedQty
if (requestQty != null) {
this.acceptedQty = if (this.purchaseOrderLine != null && this.item?.id != null) {
itemUomService.convertPurchaseQtyToStockQty(this.item!!.id!!, requestQty)
} else {
requestQty
}
}
}
// Set demandQty based on source
if (this.jobOrder != null && this.jobOrder?.bom != null) {
@@ -710,8 +721,8 @@ open class StockInLineService(
)
val purchaseItemUom = itemUomRepository.findByItemIdAndPurchaseUnitIsTrueAndDeletedIsFalse(request.itemId)

val convertedBaseQty = if (stockInLine.purchaseOrderLine != null) {
// PO-origin: qty is already stock qty
val convertedBaseQty = if (stockInLine.purchaseOrderLine != null || stockInLine.jobOrder != null) {
// PO and Job Order: qty is already stock qty
line.qty
} else if (request.stockTakeLineId == null && stockItemUom != null && purchaseItemUom != null) {
// Legacy: treat as purchase qty, convert to stock qty


불러오는 중...
취소
저장