釐清 inventoryLotLineId=實際批,對齊 actualPickLotLineId(無行為變更) tab 數量 0 不顯示 (0);Badge→文字 (n);欄位「工單編號/貨品編號」;中文 i18n(工單流程、預計生產日期、含未按規劃完成…)production
| @@ -2370,7 +2370,7 @@ private fun updateInventoryAfterLotLineChange(lotLine: InventoryLotLine) { | |||
| e.printStackTrace() | |||
| } | |||
| } | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 */ | |||
| private fun createStockLedgerForStockOut( | |||
| stockOutLine: StockOutLine, | |||
| ledgerType: String? = null, | |||
| @@ -2875,7 +2875,7 @@ open fun submitIssueWithQty(request: SubmitIssueWithQtyRequest): MessageResponse | |||
| } | |||
| // New: Create stock ledger with explicit balance (to avoid double subtraction) | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 */ | |||
| private fun createStockLedgerForStockOutWithBalance( | |||
| stockOutLine: StockOutLine, | |||
| balance: Double, | |||
| @@ -8,10 +8,10 @@ class DoUserPickAuditReportService( | |||
| private val jdbcDao: JdbcDao, | |||
| ) { | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.0 | 2026-08-03 | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.1 | 2026-08-05 | |||
| * DO workbench user-audit rows (Just issue only). | |||
| * issueTypes may combine: 不正常已完成+超出提料+未完全提料+不正常換批. | |||
| * 未完全提料/超出提料 use POL-level SUM(stock_out_line.qty) vs pol.qty (multi-lot split OK if total matches). | |||
| * issueTypes may combine: 跳過提料(使用「已完成」)+多提+少提+擅自換批/揀錯批. | |||
| * 少提/多提 use POL-level SUM(stock_out_line.qty) vs pol.qty (multi-lot split OK if total matches). | |||
| * Only completed tickets (`dop.ticketStatus = completed`) are included. | |||
| * | |||
| * Perf: filter day/completed SOL first (`day_sol`) by pickTime only (idx_sol_pickTime_deleted), | |||
| @@ -120,14 +120,14 @@ class DoUserPickAuditReportService( | |||
| IFNULL(d.isIssueJustComplete, 0) AS issueJustComplete, | |||
| COALESCE(u.name, d.handlerName, '') AS user, | |||
| TRIM(BOTH '+' FROM CONCAT_WS('+', | |||
| CASE WHEN IFNULL(d.isIssueJustComplete, 0) = 1 THEN '不正常已完成' ELSE NULL END, | |||
| CASE WHEN IFNULL(d.isIssueJustComplete, 0) = 1 THEN '跳過提料(使用「已完成」)' ELSE NULL END, | |||
| CASE WHEN d.polQty IS NOT NULL | |||
| AND COALESCE(pp.pickedTotal, 0) > d.polQty THEN '超出提料' ELSE NULL END, | |||
| AND COALESCE(pp.pickedTotal, 0) > d.polQty THEN '多提' ELSE NULL END, | |||
| CASE WHEN d.polQty IS NOT NULL | |||
| AND COALESCE(pp.pickedTotal, 0) < d.polQty | |||
| AND d.inventoryLotLineId IS NOT NULL THEN '未完全提料' ELSE NULL END, | |||
| AND d.inventoryLotLineId IS NOT NULL THEN '少提' ELSE NULL END, | |||
| CASE WHEN sug_lot.lotNo IS NOT NULL AND act_lot.lotNo IS NOT NULL | |||
| AND sug_lot.lotNo <> act_lot.lotNo THEN '不正常換批' ELSE NULL END | |||
| AND sug_lot.lotNo <> act_lot.lotNo THEN '擅自換批/揀錯批' ELSE NULL END | |||
| )) AS issueTypes, | |||
| d.solId AS stockOutLineId | |||
| FROM day_sol d | |||
| @@ -183,7 +183,7 @@ class DoUserPickAuditReportService( | |||
| return jdbcDao.queryForList(sql, args) as List<Map<String, Any?>> | |||
| } | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.1 | 2026-08-05 */ | |||
| fun getDistinctHandlers(): List<String> { | |||
| val sql = """ | |||
| SELECT DISTINCT TRIM(COALESCE(u.name, dop.handlerName, '')) AS handler | |||
| @@ -23,11 +23,11 @@ import java.io.ByteArrayOutputStream | |||
| class DoUserPickAuditReportController( | |||
| private val doUserPickAuditReportService: DoUserPickAuditReportService, | |||
| ) { | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.1 | 2026-08-05 */ | |||
| @GetMapping("/do-user-pick-audit-handlers") | |||
| fun handlers(): List<String> = doUserPickAuditReportService.getDistinctHandlers() | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.1 | 2026-08-05 */ | |||
| @GetMapping("/print-do-user-pick-audit-excel") | |||
| fun exportExcel( | |||
| @RequestParam(required = false) dateStart: String?, | |||
| @@ -93,9 +93,9 @@ class DoUserPickAuditReportController( | |||
| "actualQty" to "實際提料數量", | |||
| "lotBeforeQty" to "提料前庫存", | |||
| "lotAfterQty" to "提料後庫存", | |||
| "issueJustComplete" to "不正常已完成", | |||
| "issueJustComplete" to "跳過提料(使用「已完成」)", | |||
| "user" to "提料人", | |||
| "issueTypes" to "問題類型", | |||
| "issueTypes" to "偏離類型", | |||
| ) | |||
| var r = 0 | |||
| @@ -21,7 +21,20 @@ open class StockLedger: BaseEntity<Long>() { | |||
| @JoinColumn(name = "stockOutLineId") | |||
| open var stockOutLine: StockOutLine? = null | |||
| /** Lot line for this movement; prefer this over resolving via stock in/out line. */ | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 | |||
| * | |||
| * stock_ledger.inventoryLotLineId = the **actual** inventory_lot_line.id this | |||
| * in/out movement posted against (the lot whose qty changed). | |||
| * | |||
| * Not a suggested lot. For pick-order flows (FG / JO / PO), this must align with | |||
| * suggested_pick_lot.actualPickLotLineId (and stock_out_line.inventoryLotLineId), | |||
| * NOT suggested_pick_lot.suggestedLotLineId. | |||
| * If the picker switches lot (suggested A, scan B), ledger points to B. | |||
| * | |||
| * Prefer reading this column directly; do not infer lot only via stockInLine / | |||
| * stockOutLine when this is populated. | |||
| */ | |||
| @ManyToOne | |||
| @JoinColumn(name = "inventoryLotLineId") | |||
| open var inventoryLotLine: InventoryLotLine? = null | |||
| @@ -40,11 +53,17 @@ open class StockLedger: BaseEntity<Long>() { | |||
| @Column(name = "balance") | |||
| open var balance: Double? = null | |||
| /** Lot available qty (in−out) before this movement; null for pre-V2 rows. */ | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 | |||
| * Available qty of that same actual lot (in−out) **before** this movement; null for pre-V2 rows. | |||
| */ | |||
| @Column(name = "lotQtyBefore", precision = 14, scale = 2) | |||
| open var lotQtyBefore: BigDecimal? = null | |||
| /** Lot available qty (in−out) after this movement; null for pre-V2 rows. */ | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 | |||
| * Available qty of that same actual lot (in−out) **after** this movement; null for pre-V2 rows. | |||
| */ | |||
| @Column(name = "lotQtyAfter", precision = 14, scale = 2) | |||
| open var lotQtyAfter: BigDecimal? = null | |||
| @@ -29,7 +29,14 @@ open class SuggestedPickLot: BaseEntity<Long>() { | |||
| @JoinColumn(name = "suggestedLotLineId") | |||
| open var suggestedLotLine: InventoryLotLine? = null | |||
| /** Actual picked lot line; switch/scan updates this without changing [suggestedLotLine]. */ | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 33 | v1.0.2 | 2026-08-03 | |||
| * | |||
| * Actual picked inventory_lot_line.id. | |||
| * On create: init = suggestedLotLineId; lot switch/scan updates this only (suggested stays). | |||
| * Downstream (display, COALESCE reads, stock_ledger.inventoryLotLineId / SOL.inventoryLotLineId) | |||
| * must use this, not [suggestedLotLine]. | |||
| */ | |||
| @ManyToOne | |||
| @JoinColumn(name = "actualPickLotLineId") | |||
| open var actualPickLotLine: InventoryLotLine? = null | |||
| @@ -1378,7 +1378,7 @@ open class StockInLineService( | |||
| } | |||
| @Transactional | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 */ | |||
| private fun createStockLedgerForStockIn(stockInLine: StockInLine, inQty: Double) { | |||
| val _t0 = System.nanoTime() | |||
| fun _msSince(t: Long): Double = (System.nanoTime() - t) / 1_000_000.0 | |||
| @@ -5,15 +5,28 @@ import com.ffii.fpsms.modules.stock.entity.StockLedger | |||
| import java.math.BigDecimal | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 | |||
| * Fills [StockLedger.inventoryLotLine], [lotQtyBefore], [lotQtyAfter]. | |||
| * Prefer explicit [inventoryLotLine]; else fall back to stockOutLine / stockInLine related lot. | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 | |||
| * | |||
| * [lotAvailableAfterMove] = lot (in−out) **after** this movement has already been applied on the lot line. | |||
| * [signedDelta] = +inQty or −outQty for this ledger row (stock-in positive, stock-out negative of posted qty). | |||
| * Fills [StockLedger.inventoryLotLine], [lotQtyBefore], [lotQtyAfter] for each movement. | |||
| * | |||
| * [StockLedger.inventoryLotLine] / inventoryLotLineId = **actual** moved lot line id | |||
| * (the lot whose qty changed). Prefer explicit [inventoryLotLine]; else fall back to | |||
| * stockOutLine / stockInLine related lot. | |||
| * | |||
| * On pick/suggestion flows: pass/resolve the lot that was actually picked/posted | |||
| * (= suggested_pick_lot.actualPickLotLineId / stock_out_line.inventoryLotLineId), | |||
| * never suggested_pick_lot.suggestedLotLineId alone. Lot switch (suggested A, scan B) | |||
| * → ledger must point to B. | |||
| * | |||
| * [lotQtyBefore]/[lotQtyAfter] are snapshots of that same actual lot's available qty. | |||
| * [lotAvailableAfterMove] = lot (in−out) **after** this movement has already been applied. | |||
| * [signedDelta] = +inQty or −outQty for this ledger row (stock-in positive, stock-out negative). | |||
| */ | |||
| object StockLedgerLotSnapshot { | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 */ | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 | |||
| * Sets ledger.inventoryLotLine to the actual lot, then lotQtyBefore/After from that lot. | |||
| */ | |||
| fun apply( | |||
| ledger: StockLedger, | |||
| inventoryLotLine: InventoryLotLine? = null, | |||
| @@ -34,12 +47,12 @@ object StockLedgerLotSnapshot { | |||
| ledger.lotQtyBefore = lotAvailableAfterMove.subtract(signedDelta) | |||
| } | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 */ | |||
| fun availableQty(ill: InventoryLotLine): BigDecimal = | |||
| (ill.inQty ?: BigDecimal.ZERO).subtract(ill.outQty ?: BigDecimal.ZERO) | |||
| /** | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 | |||
| * FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 | |||
| * Ledger UOM must follow the lot line's stock ItemUom → uom.id | |||
| * (same item may have lots with different UOMs; item stockUnit flag can change later). | |||
| */ | |||
| @@ -1509,7 +1509,7 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse { | |||
| } | |||
| } | |||
| @Transactional | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 */ | |||
| private fun createStockLedgerForStockOut(stockOutLine: StockOutLine) { | |||
| val item = stockOutLine.item ?: return | |||
| val inventory = itemUomService.findInventoryForItemBaseUom(item.id!!) ?: return | |||
| @@ -1757,7 +1757,7 @@ open fun newBatchSubmit(request: QrPickBatchSubmitRequest): MessageResponse { | |||
| } | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 */ | |||
| private fun createStockLedgerForPickDelta( | |||
| stockOutLine: StockOutLine, | |||
| deltaQty: BigDecimal, | |||
| @@ -2488,7 +2488,7 @@ private fun applyVarianceAdjustment( | |||
| } | |||
| val newBalance = previousBalance - qtyToRemove.toDouble() | |||
| // FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.0 | 2026-08-03 | |||
| // FP-MTMS Version Checklist | Functions Ref. No. 46 | v1.0.1 | 2026-08-05 | |||
| // Snapshot from live lot line (in−out) at approve time — not stock_take_record.bookQty. | |||
| val lotAfterAvail = StockLedgerLotSnapshot.availableQty(latestLine).subtract(qtyToRemove) | |||