|
|
@@ -209,204 +209,6 @@ open class ReportService( |
|
|
return jdbcDao.queryForList(sql, args) |
|
|
return jdbcDao.queryForList(sql, args) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Queries the database for Semi FG Production Analysis Report data. |
|
|
|
|
|
* Flow: |
|
|
|
|
|
* 1. Filter bom by description (FG/WIP) to get bom.code values |
|
|
|
|
|
* 2. Match bom.code with stock_ledger.itemCode |
|
|
|
|
|
* 3. Aggregate stock_ledger data by month for each item based on inQty |
|
|
|
|
|
* Supports comma-separated values for stockCategory, stockSubCategory, and itemCode. |
|
|
|
|
|
*/ |
|
|
|
|
|
fun searchSemiFGProductionAnalysisReport( |
|
|
|
|
|
stockCategory: String?, |
|
|
|
|
|
stockSubCategory: String?, |
|
|
|
|
|
itemCode: String?, |
|
|
|
|
|
year: String?, |
|
|
|
|
|
lastOutDateStart: String?, |
|
|
|
|
|
lastOutDateEnd: String? |
|
|
|
|
|
): List<Map<String, Any>> { |
|
|
|
|
|
val args = mutableMapOf<String, Any>() |
|
|
|
|
|
|
|
|
|
|
|
// Filter by stockCategory from bom.description (FG/WIP) - this finds which bom.code values match |
|
|
|
|
|
// Supports multiple categories separated by comma (e.g., "FG,WIP") |
|
|
|
|
|
// If "All" is selected or contains "All", don't filter by description |
|
|
|
|
|
val stockCategorySql = if (!itemCode.isNullOrBlank()) { |
|
|
|
|
|
// When itemCode is provided, skip stockCategory filter |
|
|
|
|
|
"" |
|
|
|
|
|
} else 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 -> |
|
|
|
|
|
val paramName = "stockCategory_$index" |
|
|
|
|
|
args[paramName] = cat |
|
|
|
|
|
"b.description = :$paramName" |
|
|
|
|
|
} |
|
|
|
|
|
"AND (${conditions.joinToString(" OR ")})" |
|
|
|
|
|
} else { |
|
|
|
|
|
"" |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
"" |
|
|
|
|
|
} |
|
|
|
|
|
val stockSubCategorySql = buildMultiValueLikeClause(stockSubCategory, "ic.sub", "stockSubCategory", args) |
|
|
|
|
|
// Filter by itemCode - match bom.code (user input should match bom.code, which then matches stock_ledger.itemCode) |
|
|
|
|
|
val itemCodeSql = buildMultiValueExactClause(itemCode, "b.code", "itemCode", args) |
|
|
|
|
|
|
|
|
|
|
|
val yearSql = if (!year.isNullOrBlank()) { |
|
|
|
|
|
args["year"] = year |
|
|
|
|
|
"AND YEAR(sl.modified) = :year" |
|
|
|
|
|
} else { |
|
|
|
|
|
"" |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val lastOutDateStartSql = if (!lastOutDateStart.isNullOrBlank()) { |
|
|
|
|
|
args["lastOutDateStart"] = lastOutDateStart |
|
|
|
|
|
"AND DATE(sl.modified) >= :lastOutDateStart" |
|
|
|
|
|
} else "" |
|
|
|
|
|
|
|
|
|
|
|
val lastOutDateEndSql = if (!lastOutDateEnd.isNullOrBlank()) { |
|
|
|
|
|
args["lastOutDateEnd"] = lastOutDateEnd |
|
|
|
|
|
"AND DATE(sl.modified) < :lastOutDateEnd" |
|
|
|
|
|
} else "" |
|
|
|
|
|
|
|
|
|
|
|
val sql = """ |
|
|
|
|
|
SELECT |
|
|
|
|
|
COALESCE(ic.sub, '') as stockSubCategory, |
|
|
|
|
|
COALESCE(sl.itemCode, '') as itemNo, |
|
|
|
|
|
COALESCE(b.name, '') as itemName, |
|
|
|
|
|
COALESCE(uc.code, '') as unitOfMeasure, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 1 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyJan, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 2 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyFeb, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 3 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyMar, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 4 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyApr, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 5 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyMay, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 6 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyJun, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 7 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyJul, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 8 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyAug, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 9 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtySep, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 10 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyOct, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 11 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyNov, |
|
|
|
|
|
CAST(COALESCE(SUM(CASE WHEN MONTH(sl.modified) = 12 THEN sl.inQty ELSE 0 END), 0) AS DECIMAL(18,2)) as qtyDec, |
|
|
|
|
|
CAST(COALESCE(SUM(sl.inQty), 0) AS CHAR) as totalProductionQty |
|
|
|
|
|
FROM stock_ledger sl |
|
|
|
|
|
INNER JOIN bom b ON sl.itemCode = b.code AND b.deleted = false |
|
|
|
|
|
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 |
|
|
|
|
|
$stockCategorySql |
|
|
|
|
|
$stockSubCategorySql |
|
|
|
|
|
$itemCodeSql |
|
|
|
|
|
$yearSql |
|
|
|
|
|
$lastOutDateStartSql |
|
|
|
|
|
$lastOutDateEndSql |
|
|
|
|
|
GROUP BY sl.itemCode, ic.sub, it.id, b.name, uc.code, b.description |
|
|
|
|
|
ORDER BY ic.sub, sl.itemCode |
|
|
|
|
|
""".trimIndent() |
|
|
|
|
|
|
|
|
|
|
|
return jdbcDao.queryForList(sql, args) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Gets list of item codes (bom.code) with names based on stockCategory filter. |
|
|
|
|
|
* Supports multiple categories separated by comma (e.g., "FG,WIP"). |
|
|
|
|
|
* If stockCategory is "All" or null, returns all codes. |
|
|
|
|
|
* If stockCategory is "FG" or "WIP" or "FG,WIP", returns codes matching those descriptions. |
|
|
|
|
|
* Returns a list of maps with "code" and "name" keys. |
|
|
|
|
|
*/ |
|
|
|
|
|
fun getSemiFGItemCodes(stockCategory: String?): List<Map<String, String>> { |
|
|
|
|
|
val args = mutableMapOf<String, Any>() |
|
|
|
|
|
|
|
|
|
|
|
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 -> |
|
|
|
|
|
val paramName = "stockCategory_$index" |
|
|
|
|
|
args[paramName] = cat |
|
|
|
|
|
"b.description = :$paramName" |
|
|
|
|
|
} |
|
|
|
|
|
"AND (${conditions.joinToString(" OR ")})" |
|
|
|
|
|
} else { |
|
|
|
|
|
"" |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
"" |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val sql = """ |
|
|
|
|
|
SELECT DISTINCT b.code, COALESCE(b.name, '') as name |
|
|
|
|
|
FROM bom b |
|
|
|
|
|
WHERE b.deleted = false |
|
|
|
|
|
AND b.code IS NOT NULL |
|
|
|
|
|
AND b.code != '' |
|
|
|
|
|
$stockCategorySql |
|
|
|
|
|
ORDER BY b.code |
|
|
|
|
|
""".trimIndent() |
|
|
|
|
|
|
|
|
|
|
|
val results = jdbcDao.queryForList(sql, args) |
|
|
|
|
|
return results.mapNotNull { |
|
|
|
|
|
val code = it["code"]?.toString() |
|
|
|
|
|
val name = it["name"]?.toString() ?: "" |
|
|
|
|
|
if (code != null) { |
|
|
|
|
|
mapOf("code" to code, "name" to name) |
|
|
|
|
|
} else { |
|
|
|
|
|
null |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Gets list of item codes with their category (FG/WIP) and name based on stockCategory filter. |
|
|
|
|
|
* Supports multiple categories separated by comma (e.g., "FG,WIP"). |
|
|
|
|
|
* Returns a list of maps with "code", "category", and "name" keys. |
|
|
|
|
|
*/ |
|
|
|
|
|
fun getSemiFGItemCodesWithCategory(stockCategory: String?): List<Map<String, String>> { |
|
|
|
|
|
val args = mutableMapOf<String, Any>() |
|
|
|
|
|
|
|
|
|
|
|
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 -> |
|
|
|
|
|
val paramName = "stockCategory_$index" |
|
|
|
|
|
args[paramName] = cat |
|
|
|
|
|
"b.description = :$paramName" |
|
|
|
|
|
} |
|
|
|
|
|
"AND (${conditions.joinToString(" OR ")})" |
|
|
|
|
|
} else { |
|
|
|
|
|
"" |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
"" |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
val sql = """ |
|
|
|
|
|
SELECT DISTINCT b.code, COALESCE(b.description, '') as category, COALESCE(b.name, '') as name |
|
|
|
|
|
FROM bom b |
|
|
|
|
|
WHERE b.deleted = false |
|
|
|
|
|
AND b.code IS NOT NULL |
|
|
|
|
|
AND b.code != '' |
|
|
|
|
|
$stockCategorySql |
|
|
|
|
|
ORDER BY b.code |
|
|
|
|
|
""".trimIndent() |
|
|
|
|
|
|
|
|
|
|
|
val results = jdbcDao.queryForList(sql, args) |
|
|
|
|
|
return results.mapNotNull { |
|
|
|
|
|
val code = it["code"]?.toString() |
|
|
|
|
|
val category = it["category"]?.toString() ?: "" |
|
|
|
|
|
val name = it["name"]?.toString() ?: "" |
|
|
|
|
|
if (code != null) { |
|
|
|
|
|
mapOf("code" to code, "category" to category, "name" to name) |
|
|
|
|
|
} else { |
|
|
|
|
|
null |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Compiles and fills a Jasper Report, returning the PDF as a ByteArray. |
|
|
* Compiles and fills a Jasper Report, returning the PDF as a ByteArray. |
|
|
|