"AND si.productionDate IS NOT NULL AND DATE(si.productionDate) <= DATE(:lastOutDateEnd)"
} else ""
} else ""
val sql = """
val sql = """
WITH base AS (
WITH qr_agg AS (
SELECT
SELECT
COALESCE(sl.itemCode, '') as itemNo,
COALESCE(b.name, '') as itemName,
COALESCE(ic.sub, '') as stockSubCategory,
COALESCE(uc.udfudesc, '') as unitOfMeasure,
MONTH(si.productionDate) as mon,
si.id as stockInLineId,
si.acceptedQty as acceptedQty,
si.jobOrderId as jobOrderId
FROM stock_ledger sl
INNER JOIN bom b
ON sl.itemCode = b.code AND b.deleted = false
INNER JOIN stock_in_line si
ON si.id = sl.stockInLineId
AND si.deleted = false
AND si.productionDate IS NOT NULL
LEFT JOIN items it
ON sl.itemId = it.id
LEFT JOIN item_category ic
ON it.categoryId = ic.id
LEFT JOIN item_uom iu
ON it.id = iu.itemId
AND iu.stockUnit = true
LEFT JOIN uom_conversion uc
ON iu.uomId = uc.id
WHERE sl.deleted = false
AND sl.inQty IS NOT NULL
AND sl.inQty > 0
qr.stockInLineId,
MAX(CASE WHEN qr.qcPassed = 0 THEN 1 ELSE 0 END) AS qcFailed
FROM qc_result qr
WHERE qr.deleted = 0
GROUP BY qr.stockInLineId
),
pa_sil AS (
SELECT inventoryLotId, SUM(COALESCE(inQty, 0)) AS putAwayQtySum
FROM inventory_lot_line
WHERE deleted = false
GROUP BY inventoryLotId
),
base AS (
SELECT
COALESCE(it.code, '') AS itemNo,
COALESCE(it.name, '') AS itemName,
COALESCE(ic.sub, '') AS stockSubCategory,
COALESCE(uc.udfudesc, '') AS unitOfMeasure,
MONTH(si.productionDate) AS mon,
si.id AS stockInLineId,
CASE WHEN COALESCE(qr_agg.qcFailed, 0) = 1 THEN 0
ELSE COALESCE(pa_sil.putAwayQtySum, 0)
END AS linePutAwayQty
FROM stock_in_line si
INNER JOIN items it ON si.itemId = it.id
INNER JOIN bom b ON b.code = it.code AND b.deleted = false
LEFT JOIN qr_agg ON qr_agg.stockInLineId = si.id
LEFT JOIN pa_sil ON pa_sil.inventoryLotId = si.inventoryLotId
LEFT JOIN item_category ic ON it.categoryId = ic.id
LEFT JOIN item_uom iu ON it.id = iu.itemId AND iu.stockUnit = true
LEFT JOIN uom_conversion uc ON iu.uomId = uc.id
WHERE si.deleted = false
AND si.productionDate IS NOT NULL
$stockCategorySql
$stockCategorySql
$stockSubCategorySql
$stockSubCategorySql
$itemCodeSql
$itemCodeSql
@@ -155,7 +147,6 @@ class SemiFGProductionAnalysisReportService(
$lastOutDateStartSql
$lastOutDateStartSql
$lastOutDateEndSql
$lastOutDateEndSql
),
),
-- Deduplicate: stock_in_line can join to multiple stock_ledger rows; acceptedQty must be counted once per stockInLineId.
dedup AS (
dedup AS (
SELECT
SELECT
itemNo,
itemNo,
@@ -164,36 +155,34 @@ class SemiFGProductionAnalysisReportService(
unitOfMeasure,
unitOfMeasure,
mon,
mon,
stockInLineId,
stockInLineId,
MAX(COALESCE(acceptedQty, 0)) as acceptedQty,
MAX(jobOrderId) as jobOrderId
MAX(linePutAwayQty) AS linePutAwayQty
FROM base
FROM base
GROUP BY itemNo, itemName, stockSubCategory, unitOfMeasure, mon, stockInLineId
GROUP BY itemNo, itemName, stockSubCategory, unitOfMeasure, mon, stockInLineId
)
)
SELECT
SELECT
MAX(d.stockSubCategory) as stockSubCategory,
d.itemNo as itemNo,
MAX(d.itemName) as itemName,
MAX(d.unitOfMeasure) as unitOfMeasure,
CAST(COALESCE(SUM(CASE WHEN d.mon = 1 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyJan,
CAST(COALESCE(SUM(CASE WHEN d.mon = 2 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyFeb,
CAST(COALESCE(SUM(CASE WHEN d.mon = 3 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyMar,
CAST(COALESCE(SUM(CASE WHEN d.mon = 4 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyApr,
CAST(COALESCE(SUM(CASE WHEN d.mon = 5 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyMay,
CAST(COALESCE(SUM(CASE WHEN d.mon = 6 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyJun,
CAST(COALESCE(SUM(CASE WHEN d.mon = 7 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyJul,
CAST(COALESCE(SUM(CASE WHEN d.mon = 8 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyAug,
CAST(COALESCE(SUM(CASE WHEN d.mon = 9 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtySep,
CAST(COALESCE(SUM(CASE WHEN d.mon = 10 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyOct,
CAST(COALESCE(SUM(CASE WHEN d.mon = 11 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyNov,
CAST(COALESCE(SUM(CASE WHEN d.mon = 12 AND d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyDec,
-- Keep as CHAR for Jasper compatibility (previous template expects String).
CAST(COALESCE(SUM(CASE WHEN d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) AS CHAR) as totalProductionQty
MAX(d.stockSubCategory) AS stockSubCategory,
d.itemNo AS itemNo,
MAX(d.itemName) AS itemName,
MAX(d.unitOfMeasure) AS unitOfMeasure,
CAST(COALESCE(SUM(CASE WHEN d.mon = 1 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyJan,
CAST(COALESCE(SUM(CASE WHEN d.mon = 2 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyFeb,
CAST(COALESCE(SUM(CASE WHEN d.mon = 3 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyMar,
CAST(COALESCE(SUM(CASE WHEN d.mon = 4 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyApr,
CAST(COALESCE(SUM(CASE WHEN d.mon = 5 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyMay,
CAST(COALESCE(SUM(CASE WHEN d.mon = 6 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyJun,
CAST(COALESCE(SUM(CASE WHEN d.mon = 7 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyJul,
CAST(COALESCE(SUM(CASE WHEN d.mon = 8 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyAug,
CAST(COALESCE(SUM(CASE WHEN d.mon = 9 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtySep,
CAST(COALESCE(SUM(CASE WHEN d.mon = 10 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyOct,
CAST(COALESCE(SUM(CASE WHEN d.mon = 11 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyNov,
CAST(COALESCE(SUM(CASE WHEN d.mon = 12 THEN d.linePutAwayQty ELSE 0 END), 0) AS DECIMAL(18,2)) AS qtyDec,
CAST(COALESCE(SUM(d.linePutAwayQty), 0) AS CHAR) AS totalProductionQty
FROM dedup d
FROM dedup d
GROUP BY d.itemNo
GROUP BY d.itemNo
HAVING COALESCE(SUM(CASE WHEN d.jobOrderId IS NOT NULL AND TRIM(CAST(d.jobOrderId AS CHAR)) <> '' THEN d.acceptedQty ELSE 0 END), 0) > 0
HAVING COALESCE(SUM(d.linePutAwayQty), 0) > 0
ORDER BY d.itemNo
ORDER BY d.itemNo
""".trimIndent()
""".trimIndent()
return jdbcDao.queryForList(sql, args)
return jdbcDao.queryForList(sql, args)
}
}
@@ -208,25 +197,15 @@ class SemiFGProductionAnalysisReportService(
val args = mutableMapOf<String, Any>()
val args = mutableMapOf<String, Any>()
val stockCategorySql = if (!stockCategory.isNullOrBlank() && stockCategory != "All" && !stockCategory.contains("All")) {
val stockCategorySql = if (!stockCategory.isNullOrBlank() && stockCategory != "All" && !stockCategory.contains("All")) {
// Handle multiple categories (comma-separated)
val categories = stockCategory.split(",").map { it.trim() }.filter { it.isNotBlank() && it != "All" }
if (categories.isNotEmpty()) {
val conditions = categories.mapIndexed { index, cat ->