From ff5218ae5844368a6b85b56af5d63b4bbe12981c Mon Sep 17 00:00:00 2001 From: Fai Luk Date: Fri, 20 Mar 2026 15:53:30 +0800 Subject: [PATCH] added po number syn m18, added qtyM18 and uomIdM18 in purchase order line, and added conversion m18 to purchase unit qty --- .../m18/service/M18PurchaseOrderService.kt | 121 +++++++++++++++--- .../ffii/fpsms/m18/web/M18TestController.kt | 6 + .../service/PlasticBagPrinterService.kt | 96 ++++++++------ .../jobOrder/web/model/PlasticPrintRequest.kt | 15 ++- .../modules/master/service/ItemUomService.kt | 21 +++ .../purchaseOrder/entity/PurchaseOrderLine.kt | 7 + .../service/PurchaseOrderLineService.kt | 3 + .../web/model/SavePurchaseOrderLineRequest.kt | 4 +- ...pdate_purchase_order_line_add_uomidm18.sql | 8 ++ ..._update_purchase_order_line_add_qtym18.sql | 7 + 10 files changed, 223 insertions(+), 65 deletions(-) create mode 100644 src/main/resources/db/changelog/changes/20260319_01_codex/01_update_purchase_order_line_add_uomidm18.sql create mode 100644 src/main/resources/db/changelog/changes/20260319_02_codex/01_update_purchase_order_line_add_qtym18.sql diff --git a/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt b/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt index 04aed1a..383f34a 100644 --- a/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt +++ b/src/main/java/com/ffii/fpsms/m18/service/M18PurchaseOrderService.kt @@ -54,6 +54,7 @@ open class M18PurchaseOrderService( // M18 API val M18_LOAD_PURCHASE_ORDER_API = "/root/api/read/po" + val M18_LOAD_PURCHASE_ORDER_BY_CODE_API = "/root/api/read/po" val M18_FETCH_PURCHASE_ORDER_LIST_API = "/search/search" // Include material po, oem po @@ -204,16 +205,82 @@ open class M18PurchaseOrderService( return purchaseOrder } + open fun getPurchaseOrderByCode(code: String): M18PurchaseOrderResponse? { + var purchaseOrder: M18PurchaseOrderResponse? = null + try { + purchaseOrder = apiCallerService.get( + M18_LOAD_PURCHASE_ORDER_BY_CODE_API, + mapOf("code" to code) + ).block() + } catch (e: Exception) { + logger.error("(Getting Po Detail By Code) Error on Function - ${e.stackTrace}") + logger.error(e.message) + } + return purchaseOrder + } + + private fun resolvePoTypeByBeId(beId: Long?): PurchaseOrderType { + return if (beId?.toString() == m18Config.BEID_TOA) PurchaseOrderType.SHOP else PurchaseOrderType.MATERIAL + } + + open fun savePurchaseOrderByCode(code: String): SyncResult { + // Follow the scheduler's original approach: + // 1) use /search/search to find the PO id by code + // 2) then sync by PO id (this reuses the same create/update logic) + val searchRequest = M18PurchaseOrderListRequest( + stSearch = "po", + params = null, + conds = "(code=equal=$code)" + ) + val poListResponse = try { + apiCallerService.get( + M18_FETCH_PURCHASE_ORDER_LIST_API, + searchRequest + ).block() + } catch (e: Exception) { + logger.error("(Getting Po List By Code) Error on Function - ${e.stackTrace}") + logger.error(e.message) + null + } + + val poValues = poListResponse?.values + if (poValues.isNullOrEmpty()) { + return SyncResult( + totalProcessed = 1, + totalSuccess = 0, + totalFail = 1, + query = "code=equal=$code" + ) + } + + val request = M18PurchaseOrderListResponseWithType( + valuesWithType = mutableListOf(Pair(PurchaseOrderType.MATERIAL, poListResponse)), + query = "code=equal=$code" + ) + + return savePurchaseOrdersWithPreparedList(purchaseOrdersWithType = request) + } + open fun savePurchaseOrders(request: M18CommonRequest) : SyncResult{ - logger.info("--------------------------------------------Start - Saving M18 Purchase Order--------------------------------------------") val purchaseOrdersWithType = getPurchaseOrdersWithType(request) - val examplePurchaseOrders = listOf(4764034L) + return savePurchaseOrdersWithPreparedList( + purchaseOrdersWithType = purchaseOrdersWithType, + preloadPurchaseOrderDetails = null + ) + } + + private fun savePurchaseOrdersWithPreparedList( + purchaseOrdersWithType: M18PurchaseOrderListResponseWithType?, + preloadPurchaseOrderDetails: Map? = null + ): SyncResult { + logger.info("--------------------------------------------Start - Saving M18 Purchase Order--------------------------------------------") val successList = mutableListOf() val successDetailList = mutableListOf() val failList = mutableListOf() val failDetailList = mutableListOf() val affectedItemIds = mutableSetOf() + val uomByM18IdCache = mutableMapOf() val poRefType = "Purchase Order" val poLineRefType = "Purchase Order Line" @@ -221,7 +288,6 @@ open class M18PurchaseOrderService( if (purchaseOrdersWithType != null) { // Loop for Purchase Orders (values) purchaseOrdersWithType.valuesWithType.forEach { purchaseOrderWithType -> - val type = purchaseOrderWithType.first // if success val purchaseOrdersValues = purchaseOrderWithType.second?.values // if fail @@ -229,7 +295,8 @@ open class M18PurchaseOrderService( if (purchaseOrdersValues != null) { purchaseOrdersValues.forEach { purchaseOrder -> - val purchaseOrderDetail = getPurchaseOrder(purchaseOrder.id) + val purchaseOrderDetail = + preloadPurchaseOrderDetails?.get(purchaseOrder.id) ?: getPurchaseOrder(purchaseOrder.id) var purchaseOrderId: Long? = null //FP-MTMS @@ -242,10 +309,11 @@ open class M18PurchaseOrderService( // purchase_order + m18_data_log table if (mainpo != null) { + val m18PurchaseOrderId = mainpo.id // Find the latest m18 data log by m18 id & type // logger.info("${poRefType}: Finding For Latest M18 Data Log...") val latestPurchaseOrderLog = - m18DataLogService.findLatestM18DataLogWithSuccess(purchaseOrder.id, poRefType) + m18DataLogService.findLatestM18DataLogWithSuccess(m18PurchaseOrderId, poRefType) // logger.info(latestPurchaseOrderLog.toString()) // Save to m18_data_log table @@ -257,7 +325,7 @@ open class M18PurchaseOrderService( val saveM18PurchaseOrderLogRequest = SaveM18DataLogRequest( id = null, refType = poRefType, - m18Id = purchaseOrder.id, + m18Id = m18PurchaseOrderId, m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate), // dataLog = mainpoJson, statusEnum = M18DataLogStatus.NOT_PROCESS @@ -286,7 +354,7 @@ open class M18PurchaseOrderService( estimatedArrivalDate = commonUtils.timestampToLocalDateTime(mainpo.dDate), completeDate = null, status = PurchaseOrderStatus.PENDING.value, - type = type.value, + type = resolvePoTypeByBeId(mainpo.beId).value, m18DataLogId = saveM18PurchaseOrderLog.id, m18BeId = mainpo.beId ) @@ -305,13 +373,13 @@ open class M18PurchaseOrderService( m18DataLogService.saveM18DataLog(successSaveM18PurchaseOrderLogRequest) // log success info - successList.add(purchaseOrder.id) - logger.info("${poRefType}: Saved purchase order. ID: ${savePurchaseOrderResponse.id} | M18 ${poRefType} ID: ${purchaseOrder.id}") + successList.add(m18PurchaseOrderId) + logger.info("${poRefType}: Saved purchase order. ID: ${savePurchaseOrderResponse.id} | M18 ${poRefType} ID: ${m18PurchaseOrderId}") } catch (e: Exception) { - failList.add(purchaseOrder.id) + failList.add(m18PurchaseOrderId) // logger.error("${poRefType}: Saving Failure!") - logger.error("Error on Function - ${e.stackTrace} | Type: ${poRefType} | M18 ID: ${purchaseOrder.id} | Different? ${mainpo.id}") + logger.error("Error on Function - ${e.stackTrace} | Type: ${poRefType} | M18 ID: ${m18PurchaseOrderId} | Different? ${mainpo.id}") logger.error(e.message) val errorSaveM18PurchaseOrderLogRequest = SaveM18DataLogRequest( @@ -359,11 +427,10 @@ open class M18PurchaseOrderService( // logger.info("${poLineRefType}: Saved M18 Data Log. ID: ${saveM18PurchaseOrderLineLog.id}") // logger.info("${poLineRefType}: Finding item...") val item = itemsService.findByM18Id(line.proId) - var itemId: Long? = null - if (item == null) { - itemId = m18MasterDataService.saveProduct(line.proId)?.id + val itemId: Long? = if (item == null) { + m18MasterDataService.saveProduct(line.proId)?.id } else { - itemId = item.id + item.id } logger.info("${poLineRefType}: Item ID: ${itemId} | M18 Item ID: ${line.proId}") @@ -377,13 +444,26 @@ open class M18PurchaseOrderService( // Save to purchase_order_line table // logger.info("${poLineRefType}: Saving purchase order line...") - val itemUom = itemId?.let { itemUomService.findPurchaseUnitByItemId(it) } + val purchaseItemUom = itemId?.let { itemUomService.findPurchaseUnitByItemId(it) } + val m18ItemUom = uomByM18IdCache.getOrPut(line.unitId) { + itemUomService.findByM18Id(line.unitId) + } + val qtyM18 = line.qty + val sourceUomId = m18ItemUom?.uom?.id + // qtyM18 is the original qty in M18 line uom. + // System PO line.qty must be qty in purchase unit. + val convertedQty = if (itemId != null && sourceUomId != null) { + itemUomService.convertQtyToPurchaseQty(itemId, sourceUomId, qtyM18) + } else { + qtyM18 + } val savePurchaseOrderLineRequest = SavePurchaseOrderLineRequest( id = existingPurchaseOrderLine?.id, itemId = itemId, - uomId = itemUom?.uom?.id, + uomId = purchaseItemUom?.uom?.id, purchaseOrderId = purchaseOrderId, - qty = line.qty, + qty = convertedQty, + qtyM18 = qtyM18, up = line.up, price = line.amt, // m18CurrencyId = mainpo.curId, @@ -391,7 +471,8 @@ open class M18PurchaseOrderService( ?: PurchaseOrderLineStatus.PENDING.value, m18DataLogId = saveM18PurchaseOrderLineLog.id, m18Discount = line.disc, - m18Lot = line.lot + m18Lot = line.lot, + uomIdM18 = sourceUomId ) val savePurchaseOrderLineResponse = @@ -443,7 +524,7 @@ open class M18PurchaseOrderService( val saveM18PurchaseOrderLineLogRequest = SaveM18DataLogRequest( id = null, refType = "${poLineRefType}", - m18Id = purchaseOrder.id, + m18Id = m18PurchaseOrderId, m18LastModifyDate = commonUtils.timestampToLocalDateTime(mainpo.lastModifyDate), // dataLog = mutableMapOf(Pair("Error Message", "${poLineRefType} is null")), dataLog = mutableMapOf( diff --git a/src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt b/src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt index 668ef7a..16a18ab 100644 --- a/src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt +++ b/src/main/java/com/ffii/fpsms/m18/web/M18TestController.kt @@ -2,6 +2,7 @@ package com.ffii.fpsms.m18.web import com.ffii.core.utils.JwtTokenUtil import com.ffii.fpsms.m18.M18Config +import com.ffii.fpsms.m18.model.SyncResult import com.ffii.fpsms.m18.service.* import com.ffii.fpsms.m18.web.models.M18CommonRequest import com.ffii.fpsms.modules.common.SettingNames @@ -63,6 +64,11 @@ class M18TestController ( fun test4(): Any { return schedulerService.getM18Pos(); } + + @GetMapping("/test/po-by-code") + fun testSyncPoByCode(@RequestParam code: String): SyncResult { + return m18PurchaseOrderService.savePurchaseOrderByCode(code) + } // --------------------------------------------- Scheduler --------------------------------------------- /// // @GetMapping("/schedule/po") // fun schedulePo(@RequestParam @Valid newCron: String) { diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/PlasticBagPrinterService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/PlasticBagPrinterService.kt index b765b18..34f6d59 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/PlasticBagPrinterService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/PlasticBagPrinterService.kt @@ -581,54 +581,72 @@ open class PlasticBagPrinterService( } fun sendDataFlex6330Zpl(request: PrintRequest) { - Socket().use { socket -> - try { - // Connect with timeout - socket.connect(InetSocketAddress(request.printerIp, request.printerPort), 5000) - socket.soTimeout = 5000 // read timeout if expecting response (optional) - - val out = socket.getOutputStream() + fun zplEscape(s: String): String { + // Match python Bag2.py: escape "\" as "\\" and "^" as "\^" + return s.replace("\\", "\\\\").replace("^", "\\^") + } - // Build ZPL dynamically - val zpl = buildString { - append("^XA\n") - append("^PW500\n") // Print width ~42mm @300dpi; adjust 400-630 based on head - append("^LL280\n") // Label height ~23mm; tune for your pouch - append("^PON\n") // Normal orientation - append("^CI28\n") // UTF-8 / extended char set for Chinese + fun buildZplDataFlex(): String { + val desc = zplEscape((request.itemName.takeIf { it.isNotBlank() } ?: "—").trim()) + val code = zplEscape((request.itemCode.takeIf { it.isNotBlank() } ?: "—").trim()) - // Chinese product name / description (top) - append("^FO20,20^A@N,36,36,E:SIMSUN.FNT^FD${request.itemName}^FS\n") // Assumes font loaded + val lot = request.lotNo.trim() + val batchNo = if (lot.isBlank()) "—" else lot - // Item code - append("^FO20,80^A0N,32,32^FD${request.itemCode}^FS\n") + val labelLine = (if (lot.isBlank()) null else lot) ?: batchNo + val labelEsc = zplEscape(labelLine) - // Expiry date - append("^FO20,120^A0N,28,28^FDEXP: ${request.expiryDate}^FS\n") + val qrPayload = if (request.itemId != null && request.stockInLineId != null) { + // Keep same spacing style as python json.dumps (default). + "{\"itemId\": ${request.itemId}, \"stockInLineId\": ${request.stockInLineId}}" + } else { + if (labelLine.isNotBlank()) labelLine else batchNo + } + val qrValue = zplEscape(qrPayload) + + // Must match python Bag2.py generate_zpl_dataflex() + val fontRegular = "E:STXihei.ttf" + val fontBold = "E:STXihei.ttf" + + return """ + ^XA + ^CI28 + ^PW700 + ^LL500 + ^PO N + ^FO10,20 + ^BQN,2,4^FDQA,$qrValue^FS + ^FO170,20 + ^A@R,72,72,$fontRegular^FD$desc^FS + ^FO0,200 + ^A@R,72,72,$fontRegular^FD$labelEsc^FS + ^FO55,200 + ^A@R,88,88,$fontBold^FD$code^FS + ^XZ + """.trimIndent() + } - // Lot / Batch No. - append("^FO20,160^A0N,28,28^FDLOT: ${request.lotNo}^FS\n") + val rawQty = request.printQty + val qty = if (rawQty == -1) 100 else rawQty.coerceAtLeast(1) - // QR code encoding lotNo (or combine: item|lot|exp) - // Position right side, mag 6 (~good size), model 2 - val qrData = request.lotNo // or "${request.itemCode}|${request.lotNo}|${request.expiryDate}" - append("^FO320,20^BQN,2,6^FDQA,$qrData^FS\n") // QA, prefix for alphanumeric mode + val zpl = buildZplDataFlex() - append("^XZ\n") + repeat(qty) { idx -> + Socket().use { socket -> + try { + socket.connect(InetSocketAddress(request.printerIp, request.printerPort), 5000) + val out = socket.getOutputStream() + out.write(zpl.toByteArray(Charsets.UTF_8)) + out.flush() + } catch (e: Exception) { + throw RuntimeException( + "DataFlex ZPL communication failed (sent ${idx} of ${qty}) to ${request.printerIp}:${request.printerPort}: ${e.message}", + e + ) } - - // Send as bytes (UTF-8 safe) - out.write(zpl.toByteArray(Charsets.UTF_8)) - out.flush() - - println("DataFlex 6330 ZPL: Print job sent to ${request.printerIp}:${request.printerPort}") - // Optional: read response if printer echoes anything (rare in raw ZPL) - // val response = socket.getInputStream().readBytes().decodeToString() - // println("Response: $response") - - } catch (e: Exception) { - throw RuntimeException("DataFlex 6330 ZPL communication failed: ${e.message}", e) } } + + println("DataFlex ZPL: sent ${qty} labels to ${request.printerIp}:${request.printerPort}") } } \ No newline at end of file diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/PlasticPrintRequest.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/PlasticPrintRequest.kt index 2bc2940..c782060 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/PlasticPrintRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/web/model/PlasticPrintRequest.kt @@ -1,12 +1,17 @@ package com.ffii.fpsms.modules.jobOrder.web.model data class PrintRequest( - val itemCode: String, - val itemName: String, - val lotNo: String, - val expiryDate: String, + val itemCode: String = "—", + val itemName: String = "—", + val lotNo: String = "—", + val expiryDate: String? = null, val printerIp: String, - val printerPort: Int + val printerPort: Int, + // Used for DataFlex QR payload (same semantics as python Bag2.py) + val itemId: Long? = null, + val stockInLineId: Long? = null, + // For UI "+50/+10/+5/+1/C" printing. When -1 means continuous mode (backend prints up to 100). + val printQty: Int = 1 ) data class LaserRequest( diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt index 06f9fd9..9455edb 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ItemUomService.kt @@ -138,6 +138,27 @@ open class ItemUomService( return purchaseQty.setScale(0, RoundingMode.UP) } + /** + * Convert source quantity from a specific UOM to this item's purchase unit quantity. + * Returns source qty when source/purchase unit mapping is not found. + */ + open fun convertQtyToPurchaseQty(itemId: Long, uomId: Long, sourceQty: BigDecimal): BigDecimal { + val sourceItemUom = findFirstByItemIdAndUomId(itemId, uomId) ?: return sourceQty + val purchaseUnit = findPurchaseUnitByItemId(itemId) ?: return sourceQty + val one = BigDecimal.ONE + val calcScale = 10 + + val baseQty = sourceQty + .multiply(sourceItemUom.ratioN ?: one) + .divide(sourceItemUom.ratioD ?: one, calcScale, RoundingMode.HALF_UP) + + val purchaseQty = baseQty + .multiply(purchaseUnit.ratioD ?: one) + .divide(purchaseUnit.ratioN ?: one, calcScale, RoundingMode.HALF_UP) + + return purchaseQty.setScale(0, RoundingMode.UP) + } + open fun convertQtyToStockQty(itemId: Long, uomId: Long, sourceQty: BigDecimal): BigDecimal { val itemUom = findFirstByItemIdAndUomId(itemId, uomId) ?: return sourceQty; diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt index b387bd8..5b85e02 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/entity/PurchaseOrderLine.kt @@ -36,6 +36,9 @@ open class PurchaseOrderLine : BaseEntity() { @Column(name = "qty", precision = 14, scale = 2) open var qty: BigDecimal? = null + @Column(name = "qtyM18", precision = 14, scale = 2) + open var qtyM18: BigDecimal? = null + @Column(name = "up", precision = 14, scale = 2) open var up: BigDecimal? = null @@ -63,6 +66,10 @@ open class PurchaseOrderLine : BaseEntity() { @Column(name = "m18Lot", length = 20) open var m18Lot: String? = null + @ManyToOne + @JoinColumn(name = "uomIdM18") + open var uomM18: UomConversion? = null + @JsonManagedReference @OneToMany(mappedBy = "purchaseOrderLine", cascade = [CascadeType.ALL], orphanRemoval = true) open var stockInLines: MutableList = mutableListOf() diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt index 8909c13..9c77ff2 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/service/PurchaseOrderLineService.kt @@ -71,15 +71,18 @@ open class PurchaseOrderLineService( // val currency = request.m18CurrencyId?.let { currencyService.findByM18Id(it) } // ?: request.currencyId?.let { currencyService.findById(it) } val uom = request.uomId?.let { uomConversionService.find(it).getOrNull() } + val uomM18 = request.uomIdM18?.let { uomConversionService.find(it).getOrNull() } purchaseOrderLine.apply { this.item = item itemNo = item?.code this.purchaseOrder = purchaseOrder qty = request.qty + qtyM18 = request.qtyM18 up = request.up price = request.price this.uom = uom + this.uomM18 = uomM18 // this.currency = currency this.status = status this.m18DataLog = m18DataLog ?: this.m18DataLog diff --git a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt index 9dfa9bc..440b206 100644 --- a/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt +++ b/src/main/java/com/ffii/fpsms/modules/purchaseOrder/web/model/SavePurchaseOrderLineRequest.kt @@ -8,6 +8,7 @@ data class SavePurchaseOrderLineRequest( val uomId: Long?, val purchaseOrderId: Long?, val qty: BigDecimal?, + val qtyM18: BigDecimal?, val up: BigDecimal?, // unit price val price: BigDecimal?, // val currencyId: Long? = null, @@ -16,4 +17,5 @@ data class SavePurchaseOrderLineRequest( val m18DataLogId: Long?, val m18Discount: BigDecimal?, val m18Lot: String?, -) \ No newline at end of file + val uomIdM18: Long?, +) diff --git a/src/main/resources/db/changelog/changes/20260319_01_codex/01_update_purchase_order_line_add_uomidm18.sql b/src/main/resources/db/changelog/changes/20260319_01_codex/01_update_purchase_order_line_add_uomidm18.sql new file mode 100644 index 0000000..0af7e45 --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260319_01_codex/01_update_purchase_order_line_add_uomidm18.sql @@ -0,0 +1,8 @@ +-- liquibase formatted sql +-- changeset codex:add_uomidm18_to_po_line +-- preconditions onFail:MARK_RAN +-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'purchase_order_line' AND COLUMN_NAME = 'uomIdM18' + +ALTER TABLE `purchase_order_line` + ADD COLUMN `uomIdM18` INT NULL AFTER `uomId`, + ADD CONSTRAINT `FK_PURCHASE_ORDER_LINE_ON_UOMIDM18` FOREIGN KEY (`uomIdM18`) REFERENCES `uom_conversion` (`id`); diff --git a/src/main/resources/db/changelog/changes/20260319_02_codex/01_update_purchase_order_line_add_qtym18.sql b/src/main/resources/db/changelog/changes/20260319_02_codex/01_update_purchase_order_line_add_qtym18.sql new file mode 100644 index 0000000..c99d72b --- /dev/null +++ b/src/main/resources/db/changelog/changes/20260319_02_codex/01_update_purchase_order_line_add_qtym18.sql @@ -0,0 +1,7 @@ +-- liquibase formatted sql +-- changeset codex:add_qtym18_to_po_line +-- preconditions onFail:MARK_RAN +-- precondition-sql-check expectedResult:0 SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'purchase_order_line' AND COLUMN_NAME = 'qtyM18' + +ALTER TABLE `purchase_order_line` + ADD COLUMN `qtyM18` DECIMAL(14,2) NULL AFTER `qty`;