From 631064d2a40282e583b6d1f14f5b0a653b879456 Mon Sep 17 00:00:00 2001 From: "B.E.N.S.O.N" Date: Sat, 7 Feb 2026 19:55:22 +0800 Subject: [PATCH] Update --- .../modules/report/service/ReportService.kt | 198 ------------------ .../modules/report/web/ReportController.kt | 63 ------ 2 files changed, 261 deletions(-) diff --git a/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt b/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt index 55ef7d3..84a45f6 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt @@ -209,204 +209,6 @@ open class ReportService( 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> { - val args = mutableMapOf() - - // 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> { - val args = mutableMapOf() - - 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> { - val args = mutableMapOf() - - 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. diff --git a/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt b/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt index ceaf074..34290c3 100644 --- a/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt +++ b/src/main/java/com/ffii/fpsms/modules/report/web/ReportController.kt @@ -126,67 +126,4 @@ class ReportController( return ResponseEntity(pdfBytes, headers, HttpStatus.OK) } - @GetMapping("/print-semi-fg-production-analysis") - fun generateSemiFGProductionAnalysisReport( - @RequestParam(required = false) stockCategory: String?, - @RequestParam(required = false) stockSubCategory: String?, - @RequestParam(required = false) itemCode: String?, - @RequestParam(required = false) year: String?, - @RequestParam(required = false) lastOutDateStart: String?, - @RequestParam(required = false) lastOutDateEnd: String? - ): ResponseEntity { - val parameters = mutableMapOf() - - // Set report header parameters - parameters["stockCategory"] = stockCategory ?: "All" - parameters["stockSubCategory"] = stockSubCategory ?: "All" - parameters["itemNo"] = itemCode ?: "All" - parameters["year"] = year ?: LocalDate.now().year.toString() - parameters["reportDate"] = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) - parameters["reportTime"] = LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")) - parameters["lastOutDateStart"] = lastOutDateStart ?: "" - parameters["lastOutDateEnd"] = lastOutDateEnd ?: "" - parameters["deliveryPeriodStart"] = "" - parameters["deliveryPeriodEnd"] = "" - - // Query the DB to get a list of data - val dbData = reportService.searchSemiFGProductionAnalysisReport( - stockCategory, - stockSubCategory, - itemCode, - year, - lastOutDateStart, - lastOutDateEnd - ) - - val pdfBytes = reportService.createPdfResponse( - "/jasper/SemiFGProductionAnalysisReport.jrxml", - parameters, - dbData - ) - - val headers = HttpHeaders().apply { - contentType = MediaType.APPLICATION_PDF - setContentDispositionFormData("attachment", "SemiFGProductionAnalysisReport.pdf") - set("filename", "SemiFGProductionAnalysisReport.pdf") - } - - return ResponseEntity(pdfBytes, headers, HttpStatus.OK) - } - - @GetMapping("/semi-fg-item-codes") - fun getSemiFGItemCodes( - @RequestParam(required = false) stockCategory: String? - ): ResponseEntity>> { - val itemCodes = reportService.getSemiFGItemCodes(stockCategory) - return ResponseEntity(itemCodes, HttpStatus.OK) - } - - @GetMapping("/semi-fg-item-codes-with-category") - fun getSemiFGItemCodesWithCategory( - @RequestParam(required = false) stockCategory: String? - ): ResponseEntity>> { - val itemCodesWithCategory = reportService.getSemiFGItemCodesWithCategory(stockCategory) - return ResponseEntity(itemCodesWithCategory, HttpStatus.OK) - } } \ No newline at end of file