|
|
|
@@ -9,6 +9,8 @@ import java.time.format.DateTimeFormatter |
|
|
|
* 庫存批次現況(Stock Balance):永遠今天。 |
|
|
|
* 現存讀 [inventory_lot_line](available、未過期、in-out > 0)。 |
|
|
|
* 最後異動:有 inventoryLotLineId 的帳本用 MAX(id);缺的比 SIL/SOL 時間。 |
|
|
|
* 單位均價:貨品 AverageUnitPrice(HKD/stock UOM)用 item_uom 比率轉成該 lot 單位; |
|
|
|
* 同一貨品同一 UOM 共用同一均價。庫存總價值 = 該 UOM 均價 × lot 數量。 |
|
|
|
* 單位均價/庫存總價值只填 root PO 為 PP/PF 的批(TRF 往回走);其他來源空白。 |
|
|
|
*/ |
|
|
|
@Service |
|
|
|
@@ -70,6 +72,19 @@ lot_root_origin AS ( |
|
|
|
WHERE t.rn = 1 |
|
|
|
)""" |
|
|
|
|
|
|
|
/** One stock-unit item_uom per item (same pick as item average-price SQL). */ |
|
|
|
private const val STOCK_ONE_CTE_SQL = """ |
|
|
|
stock_one AS ( |
|
|
|
SELECT iu.* |
|
|
|
FROM item_uom iu |
|
|
|
INNER JOIN ( |
|
|
|
SELECT itemId, MIN(id) AS id |
|
|
|
FROM item_uom |
|
|
|
WHERE deleted = 0 AND stockUnit = 1 |
|
|
|
GROUP BY itemId |
|
|
|
) x ON x.id = iu.id |
|
|
|
)""" |
|
|
|
|
|
|
|
private const val ROOT_STOCK_IN_JOIN_SQL = """ |
|
|
|
LEFT JOIN lot_root_origin lro |
|
|
|
ON lro.lotId = il.id |
|
|
|
@@ -85,7 +100,7 @@ lot_root_origin AS ( |
|
|
|
val stockDate: String, |
|
|
|
) |
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.0 | 2026-08-31 */ |
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.1 | 2026-09-07 */ |
|
|
|
fun search( |
|
|
|
itemCode: String?, |
|
|
|
storeId: String?, |
|
|
|
@@ -133,12 +148,13 @@ lot_root_origin AS ( |
|
|
|
} |
|
|
|
val lotOriginFilterSql = buildLotOriginFilterSql(lotOrigin) |
|
|
|
val originJoinSql = ROOT_STOCK_IN_JOIN_SQL |
|
|
|
val ctePrefix = "WITH RECURSIVE\n$LOT_ROOT_ORIGIN_CTE_SQL\n" |
|
|
|
val ctePrefix = "WITH RECURSIVE\n$LOT_ROOT_ORIGIN_CTE_SQL,\n$STOCK_ONE_CTE_SQL\n" |
|
|
|
|
|
|
|
val liveLots = jdbcDao.queryForList( |
|
|
|
""" |
|
|
|
${ctePrefix}SELECT |
|
|
|
ill.id AS inventoryLotLineId, |
|
|
|
COALESCE(NULLIF(TRIM(ill.remarks), ''), '') AS remarks, |
|
|
|
COALESCE(it.code, '') AS itemNo, |
|
|
|
COALESCE(it.name, '') AS itemName, |
|
|
|
COALESCE(uc.udfudesc, uc.code, '') AS unitOfMeasure, |
|
|
|
@@ -149,7 +165,19 @@ lot_root_origin AS ( |
|
|
|
COALESCE(wh.area, '') AS areaPart, |
|
|
|
COALESCE(wh.slot, '') AS slotPart, |
|
|
|
(COALESCE(ill.inQty, 0) - COALESCE(ill.outQty, 0)) AS lotQtyRaw, |
|
|
|
COALESCE(CAST(NULLIF(it.AverageUnitPrice, '') AS DECIMAL(14, 4)), 0) AS avgUnitPriceRaw, |
|
|
|
CASE |
|
|
|
WHEN iu.id IS NULL OR so.id IS NULL THEN |
|
|
|
COALESCE(CAST(NULLIF(TRIM(it.AverageUnitPrice), '') AS DECIMAL(14, 4)), 0) |
|
|
|
WHEN iu.uomId = so.uomId THEN |
|
|
|
COALESCE(CAST(NULLIF(TRIM(it.AverageUnitPrice), '') AS DECIMAL(14, 4)), 0) |
|
|
|
ELSE |
|
|
|
ROUND( |
|
|
|
COALESCE(CAST(NULLIF(TRIM(it.AverageUnitPrice), '') AS DECIMAL(14, 4)), 0) |
|
|
|
* (COALESCE(iu.ratioN, 1) / COALESCE(NULLIF(iu.ratioD, 0), 1)) |
|
|
|
* (COALESCE(so.ratioD, 1) / COALESCE(NULLIF(so.ratioN, 0), 1)), |
|
|
|
4 |
|
|
|
) |
|
|
|
END AS avgUnitPriceRaw, |
|
|
|
UPPER(LEFT(TRIM(COALESCE(root_po.code, '')), 2)) AS rootPoPrefix |
|
|
|
FROM inventory_lot_line ill |
|
|
|
INNER JOIN inventory_lot il |
|
|
|
@@ -162,6 +190,8 @@ lot_root_origin AS ( |
|
|
|
ON iu.id = ill.stockItemUomId AND iu.deleted = 0 |
|
|
|
LEFT JOIN uom_conversion uc |
|
|
|
ON uc.id = iu.uomId |
|
|
|
LEFT JOIN stock_one so |
|
|
|
ON so.itemId = it.id |
|
|
|
$originJoinSql |
|
|
|
WHERE ill.deleted = 0 |
|
|
|
AND it.code IS NOT NULL AND it.code <> '' |
|
|
|
@@ -200,7 +230,7 @@ lot_root_origin AS ( |
|
|
|
|
|
|
|
private data class SilSolHit(val ts: String, val date: String, val kind: String) |
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.0 | 2026-08-31 */ |
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.1 | 2026-09-07 */ |
|
|
|
private fun loadLastTrnFromLedger(lotIds: List<Long>, out: MutableMap<Long, Pair<String, String>>) { |
|
|
|
if (lotIds.isEmpty()) return |
|
|
|
// idx_ledger_lot_date_id is (lot, date, id) — bad for MAX(id); force the lot-only index |
|
|
|
@@ -233,7 +263,7 @@ lot_root_origin AS ( |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.0 | 2026-08-31 */ |
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.1 | 2026-09-07 */ |
|
|
|
private fun fillLastTrnFromLotHeaderSil(missing: List<Long>, hits: MutableMap<Long, SilSolHit>): Int { |
|
|
|
var n = 0 |
|
|
|
for (chunk in missing.chunked(800)) { |
|
|
|
@@ -259,7 +289,7 @@ lot_root_origin AS ( |
|
|
|
return n |
|
|
|
} |
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.0 | 2026-08-31 */ |
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.1 | 2026-09-07 */ |
|
|
|
private fun fillLastTrnFromSilLine(missing: List<Long>, hits: MutableMap<Long, SilSolHit>): Int { |
|
|
|
var n = 0 |
|
|
|
for (chunk in missing.chunked(800)) { |
|
|
|
@@ -281,7 +311,7 @@ lot_root_origin AS ( |
|
|
|
return n |
|
|
|
} |
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.0 | 2026-08-31 */ |
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.1 | 2026-09-07 */ |
|
|
|
private fun fillLastTrnFromSol(missing: List<Long>, hits: MutableMap<Long, SilSolHit>): Int { |
|
|
|
var n = 0 |
|
|
|
for (chunk in missing.chunked(800)) { |
|
|
|
@@ -325,7 +355,7 @@ lot_root_origin AS ( |
|
|
|
else -> "" |
|
|
|
} |
|
|
|
|
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.0 | 2026-08-31 */ |
|
|
|
/** FP-MTMS Version Checklist | Functions Ref. No. 68 | v1.0.1 | 2026-09-07 */ |
|
|
|
private fun assembleRows( |
|
|
|
liveLots: List<Map<String, Any>>, |
|
|
|
lastTrnByLot: Map<Long, Pair<String, String>>, |
|
|
|
@@ -344,8 +374,9 @@ lot_root_origin AS ( |
|
|
|
val last = lastTrnByLot[lotId] |
|
|
|
val origin = r["rootPoPrefix"]?.toString().orEmpty().uppercase() |
|
|
|
val showPrice = origin == "PP" || origin == "PF" |
|
|
|
val row = HashMap<String, Any>(22) |
|
|
|
val row = HashMap<String, Any>(24) |
|
|
|
row["inventoryLotLineId"] = lotId.toString() |
|
|
|
row["remarks"] = r["remarks"] ?: "" |
|
|
|
row["itemNo"] = r["itemNo"] ?: "" |
|
|
|
row["itemName"] = r["itemName"] ?: "" |
|
|
|
row["unitOfMeasure"] = r["unitOfMeasure"] ?: "" |
|
|
|
|