| @@ -296,6 +296,263 @@ ORDER BY | |||||
| return result | return result | ||||
| } | } | ||||
| /** | |||||
| * V2:依盤點輪次 + 可選物料編號;僅輸出該輪已有 `stocktakerecord` 且 `status='completed'` 的列(未建立盤點紀錄的批號不列出)。 | |||||
| * 期初/累計區間取該輪紀錄之 MIN(`date`)~MAX(`date`)(與 V1 之 in/out 聚合邏輯一致,但以 rb 帶入)。 | |||||
| * 「審核時間」欄位:`approverTime` 之本地日期時間字串;無則退回盤點日 `date`。 | |||||
| */ | |||||
| fun searchStockTakeVarianceReportV2( | |||||
| stockTakeRoundId: Long, | |||||
| itemCode: String?, | |||||
| ): List<Map<String, Any>> { | |||||
| val countSql = """ | |||||
| SELECT COUNT(*) AS c FROM stocktakerecord s | |||||
| WHERE s.deleted = 0 | |||||
| AND s.stockTakeRoundId = :stockTakeRoundId | |||||
| AND s.status = 'completed' | |||||
| """.trimIndent() | |||||
| val cntRow = jdbcDao.queryForList( | |||||
| countSql, | |||||
| mapOf("stockTakeRoundId" to stockTakeRoundId) | |||||
| ).firstOrNull() | |||||
| val cnt = (cntRow?.get("c") as? Number)?.toLong() ?: 0L | |||||
| if (cnt == 0L) return emptyList() | |||||
| val args = mutableMapOf<String, Any>() | |||||
| args["stockTakeRoundId"] = stockTakeRoundId | |||||
| val itemCodeSql = buildMultiValueLikeClause( | |||||
| itemCode, | |||||
| "it.code", | |||||
| "itemCode", | |||||
| args | |||||
| ) | |||||
| val sql = """ | |||||
| WITH rb AS ( | |||||
| SELECT | |||||
| COALESCE(MIN(s.date), CURRENT_DATE) AS fromDate, | |||||
| COALESCE(MAX(s.date), CURRENT_DATE) AS toDate | |||||
| FROM stocktakerecord s | |||||
| WHERE s.deleted = 0 | |||||
| AND s.stockTakeRoundId = :stockTakeRoundId | |||||
| AND s.status = 'completed' | |||||
| ), | |||||
| latest_str AS ( | |||||
| SELECT | |||||
| str.lotId, | |||||
| str.warehouseId, | |||||
| str.bookQty, | |||||
| str.varianceQty, | |||||
| str.approverStockTakeQty, | |||||
| str.date AS strDate, | |||||
| str.id, | |||||
| str.approverTime | |||||
| FROM stocktakerecord str | |||||
| WHERE str.deleted = 0 | |||||
| AND str.stockTakeRoundId = :stockTakeRoundId | |||||
| AND str.status = 'completed' | |||||
| ), | |||||
| in_agg AS ( | |||||
| SELECT | |||||
| ill.id AS inventoryLotLineId, | |||||
| SUM(CASE WHEN DATE(sil.receiptDate) < rb.fromDate THEN | |||||
| CASE WHEN sil.purchaseOrderLineId IS NOT NULL | |||||
| THEN COALESCE(sil.acceptedQty, 0) | |||||
| WHEN iu_purchase.id IS NOT NULL AND iu_stock.id IS NOT NULL | |||||
| THEN COALESCE(sil.acceptedQty, 0) * (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) / (iu_stock.ratioN / NULLIF(iu_stock.ratioD, 0)) | |||||
| ELSE COALESCE(sil.acceptedQty, 0) | |||||
| END | |||||
| ELSE 0 END) AS inBefore, | |||||
| SUM(CASE WHEN DATE(sil.receiptDate) BETWEEN rb.fromDate AND rb.toDate THEN | |||||
| CASE WHEN sil.purchaseOrderLineId IS NOT NULL | |||||
| THEN COALESCE(sil.acceptedQty, 0) | |||||
| WHEN iu_purchase.id IS NOT NULL AND iu_stock.id IS NOT NULL | |||||
| THEN COALESCE(sil.acceptedQty, 0) * (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) / (iu_stock.ratioN / NULLIF(iu_stock.ratioD, 0)) | |||||
| ELSE COALESCE(sil.acceptedQty, 0) | |||||
| END | |||||
| ELSE 0 END) AS inDuring, | |||||
| MAX(CASE WHEN sil.receiptDate IS NOT NULL THEN DATE(sil.receiptDate) END) AS lastInDate | |||||
| FROM inventory_lot_line ill | |||||
| CROSS JOIN rb | |||||
| INNER JOIN inventory_lot il | |||||
| ON ill.inventoryLotId = il.id | |||||
| AND il.deleted = 0 | |||||
| INNER JOIN items it | |||||
| ON il.itemId = it.id | |||||
| AND it.deleted = 0 | |||||
| LEFT JOIN stock_in_line sil | |||||
| ON sil.inventoryLotLineId = ill.id | |||||
| AND sil.deleted = 0 | |||||
| AND sil.status = 'completed' | |||||
| LEFT JOIN item_uom iu_purchase | |||||
| ON it.id = iu_purchase.itemId | |||||
| AND iu_purchase.purchaseUnit = 1 | |||||
| AND iu_purchase.deleted = 0 | |||||
| LEFT JOIN item_uom iu_stock | |||||
| ON it.id = iu_stock.itemId | |||||
| AND iu_stock.stockUnit = 1 | |||||
| AND iu_stock.deleted = 0 | |||||
| WHERE ill.deleted = 0 | |||||
| GROUP BY ill.id | |||||
| ), | |||||
| out_agg AS ( | |||||
| SELECT | |||||
| ill.id AS inventoryLotLineId, | |||||
| SUM(CASE WHEN DATE(sol.endTime) < rb.fromDate THEN COALESCE(sol.qty, 0) ELSE 0 END) AS outBefore, | |||||
| SUM(CASE WHEN DATE(sol.endTime) BETWEEN rb.fromDate AND rb.toDate THEN COALESCE(sol.qty, 0) ELSE 0 END) AS outDuring, | |||||
| MAX(CASE WHEN sol.endTime IS NOT NULL THEN DATE(sol.endTime) END) AS lastOutDate | |||||
| FROM inventory_lot_line ill | |||||
| CROSS JOIN rb | |||||
| LEFT JOIN stock_out_line sol | |||||
| ON sol.inventoryLotLineId = ill.id | |||||
| AND sol.deleted = 0 | |||||
| AND sol.status = 'completed' | |||||
| WHERE ill.deleted = 0 | |||||
| GROUP BY ill.id | |||||
| ), | |||||
| in_out AS ( | |||||
| SELECT | |||||
| i.inventoryLotLineId, | |||||
| COALESCE(i.inBefore, 0) AS inBefore, | |||||
| COALESCE(o.outBefore, 0) AS outBefore, | |||||
| COALESCE(i.inDuring, 0) AS inDuring, | |||||
| COALESCE(o.outDuring, 0) AS outDuring, | |||||
| i.lastInDate, | |||||
| o.lastOutDate | |||||
| FROM in_agg i | |||||
| LEFT JOIN out_agg o ON o.inventoryLotLineId = i.inventoryLotLineId | |||||
| ), | |||||
| data AS ( | |||||
| SELECT | |||||
| it.type AS stockSubCategory, | |||||
| it.code AS itemNo, | |||||
| it.name AS itemName, | |||||
| uc.udfudesc AS unitOfMeasure, | |||||
| il.lotNo AS lotNo, | |||||
| COALESCE(DATE_FORMAT(il.expiryDate, '%Y-%m-%d'), '') AS expiryDate, | |||||
| wh.code AS storeLocation, | |||||
| (COALESCE(io.inBefore, 0) - COALESCE(io.outBefore, 0)) AS openingQty, | |||||
| COALESCE(io.inDuring, 0) AS inQty, | |||||
| COALESCE(io.outDuring, 0) AS outQty, | |||||
| ((COALESCE(io.inBefore, 0) - COALESCE(io.outBefore, 0)) + COALESCE(io.inDuring, 0) - COALESCE(io.outDuring, 0)) AS currentQty, | |||||
| io.lastInDate AS lastInDateRaw, | |||||
| io.lastOutDate AS lastOutDateRaw, | |||||
| ls.bookQty AS stkBookQty, | |||||
| ls.approverStockTakeQty AS stkApproverQty, | |||||
| ls.varianceQty AS stkVarianceQty, | |||||
| ls.strDate AS stockTakeDateRaw, | |||||
| ls.approverTime AS approvalDateTimeRaw | |||||
| FROM latest_str ls | |||||
| INNER JOIN inventory_lot il | |||||
| ON ls.lotId = il.id | |||||
| AND il.deleted = 0 | |||||
| INNER JOIN inventory_lot_line ill | |||||
| ON ill.inventoryLotId = il.id | |||||
| AND ill.warehouseId = ls.warehouseId | |||||
| AND ill.deleted = 0 | |||||
| INNER JOIN items it | |||||
| ON il.itemId = it.id | |||||
| AND it.deleted = 0 | |||||
| INNER JOIN warehouse wh | |||||
| ON wh.id = ls.warehouseId | |||||
| AND wh.deleted = 0 | |||||
| LEFT JOIN item_uom iu | |||||
| ON it.id = iu.itemId | |||||
| AND iu.stockUnit = 1 | |||||
| AND iu.deleted = 0 | |||||
| LEFT JOIN uom_conversion uc | |||||
| ON iu.uomId = uc.id | |||||
| LEFT JOIN in_out io | |||||
| ON io.inventoryLotLineId = ill.id | |||||
| WHERE 1=1 | |||||
| $itemCodeSql | |||||
| ) | |||||
| SELECT | |||||
| stockSubCategory, | |||||
| itemNo, | |||||
| itemName, | |||||
| unitOfMeasure, | |||||
| lotNo, | |||||
| expiryDate, | |||||
| storeLocation, | |||||
| CASE WHEN COALESCE(openingQty, 0) < 0 THEN CONCAT('(', FORMAT(-openingQty, 0), ')') ELSE FORMAT(COALESCE(openingQty, 0), 0) END AS openingBalance, | |||||
| CASE WHEN COALESCE(inQty, 0) < 0 THEN CONCAT('(', FORMAT(-inQty, 0), ')') ELSE FORMAT(COALESCE(inQty, 0), 0) END AS cumStockIn, | |||||
| CASE WHEN COALESCE(outQty, 0) < 0 THEN CONCAT('(', FORMAT(-outQty, 0), ')') ELSE FORMAT(COALESCE(outQty, 0), 0) END AS cumStockOut, | |||||
| CASE WHEN COALESCE(stkBookQty, 0) < 0 THEN CONCAT('(', FORMAT(-stkBookQty, 0), ')') ELSE FORMAT(COALESCE(stkBookQty, 0), 0) END AS currentBookBalance, | |||||
| COALESCE(DATE_FORMAT(lastInDateRaw, '%Y-%m-%d'), '') AS lastInDate, | |||||
| COALESCE(DATE_FORMAT(lastOutDateRaw, '%Y-%m-%d'), '') AS lastOutDate, | |||||
| COALESCE( | |||||
| DATE_FORMAT(approvalDateTimeRaw, '%Y-%m-%d %H:%i:%s'), | |||||
| COALESCE(DATE_FORMAT(stockTakeDateRaw, '%Y-%m-%d'), '') | |||||
| ) AS stockTakeDate, | |||||
| CASE | |||||
| WHEN stkApproverQty IS NULL THEN '0' | |||||
| WHEN COALESCE(stkApproverQty, 0) < 0 THEN CONCAT('(', FORMAT(-stkApproverQty, 0), ')') | |||||
| ELSE FORMAT(COALESCE(stkApproverQty, 0), 0) | |||||
| END AS stockTakeQty, | |||||
| CASE | |||||
| WHEN stkVarianceQty IS NULL THEN '0' | |||||
| WHEN COALESCE(stkVarianceQty, 0) < 0 THEN CONCAT('(', FORMAT(-stkVarianceQty, 0), ')') | |||||
| ELSE FORMAT(COALESCE(stkVarianceQty, 0), 0) | |||||
| END AS variance, | |||||
| CASE | |||||
| WHEN stkVarianceQty IS NULL THEN '0%' | |||||
| WHEN COALESCE(stkBookQty, 0) = 0 THEN '0%' | |||||
| WHEN (COALESCE(stkVarianceQty, 0) / stkBookQty) * 100 < 0 THEN CONCAT('(', FORMAT(-(COALESCE(stkVarianceQty, 0) / stkBookQty) * 100, 0), '%)') | |||||
| ELSE CONCAT(FORMAT((COALESCE(stkVarianceQty, 0) / stkBookQty) * 100, 0), '%') | |||||
| END AS variancePercentage, | |||||
| CASE WHEN SUM(COALESCE(openingQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(openingQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(openingQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalOpeningBalance, | |||||
| CASE WHEN SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(inQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalCumStockIn, | |||||
| CASE WHEN SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(outQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalCumStockOut, | |||||
| CASE WHEN SUM(COALESCE(stkBookQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(stkBookQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(stkBookQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalCurrentBalance | |||||
| FROM data | |||||
| ORDER BY | |||||
| itemNo, | |||||
| lotNo, | |||||
| storeLocation | |||||
| """.trimIndent() | |||||
| return jdbcDao.queryForList(sql, args) | |||||
| } | |||||
| /** 報表表頭:盤點輪次說明(與 /report/stock-take-rounds 選項格式一致) */ | |||||
| fun getStockTakeRoundCaption(stockTakeRoundId: Long): String { | |||||
| val sql = """ | |||||
| SELECT CONCAT( | |||||
| 'Round ', | |||||
| CAST(st.stockTakeRoundId AS CHAR), | |||||
| ' (', | |||||
| DATE_FORMAT(MIN(st.planStart), '%Y-%m-%d'), | |||||
| ')' | |||||
| ) AS cap | |||||
| FROM stock_take st | |||||
| WHERE st.deleted = 0 | |||||
| AND st.stockTakeRoundId = :stockTakeRoundId | |||||
| GROUP BY st.stockTakeRoundId | |||||
| """.trimIndent() | |||||
| val row = jdbcDao.queryForList( | |||||
| sql, | |||||
| mapOf("stockTakeRoundId" to stockTakeRoundId) | |||||
| ).firstOrNull() | |||||
| val cap = row?.get("cap") as? String | |||||
| return if (!cap.isNullOrBlank()) cap else "Round $stockTakeRoundId" | |||||
| } | |||||
| /** LIKE 多值工具方法 */ | /** LIKE 多值工具方法 */ | ||||
| private fun buildMultiValueLikeClause( | private fun buildMultiValueLikeClause( | ||||
| paramValue: String?, | paramValue: String?, | ||||
| @@ -73,6 +73,64 @@ class StockTakeVarianceReportController( | |||||
| parameters["stockTakeDate"] = stockTakeDateDisplay | parameters["stockTakeDate"] = stockTakeDateDisplay | ||||
| parameters["stockTakeFilterCaption"] = "" | |||||
| val pdfBytes = reportService.createPdfResponse( | |||||
| "/jasper/StockTakeVarianceReport.jrxml", | |||||
| parameters, | |||||
| dbData | |||||
| ) | |||||
| val headers = HttpHeaders().apply { | |||||
| contentType = MediaType.APPLICATION_PDF | |||||
| setContentDispositionFormData("attachment", "StockTakeVarianceReport.pdf") | |||||
| set("filename", "StockTakeVarianceReport.pdf") | |||||
| } | |||||
| return ResponseEntity(pdfBytes, headers, HttpStatus.OK) | |||||
| } | |||||
| /** | |||||
| * Stock Take Variance 報表 V2:依盤點輪次 + 可選物料編號;僅含已有已完成盤點紀錄之列;審核時間為 approver 之日期時間。 | |||||
| */ | |||||
| @GetMapping("/print-stock-take-variance-v2") | |||||
| fun generateStockTakeVarianceReportV2( | |||||
| @RequestParam stockTakeRoundId: Long, | |||||
| @RequestParam(required = false) itemCode: String?, | |||||
| ): ResponseEntity<ByteArray> { | |||||
| val parameters = mutableMapOf<String, Any>() | |||||
| parameters["stockCategory"] = "All" | |||||
| parameters["stockSubCategory"] = "All" | |||||
| parameters["itemNo"] = itemCode ?: "All" | |||||
| parameters["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["storeLocation"] = "" | |||||
| parameters["balanceFilterStart"] = "" | |||||
| parameters["balanceFilterEnd"] = "" | |||||
| parameters["stockTakeDateStart"] = "" | |||||
| parameters["stockTakeDateEnd"] = "" | |||||
| parameters["lastInDateEnd"] = "" | |||||
| parameters["lastOutDateEnd"] = "" | |||||
| parameters["stockTakeFilterCaption"] = | |||||
| stockTakeVarianceReportService.getStockTakeRoundCaption(stockTakeRoundId) | |||||
| parameters["stockTakeConditionLabel"] = "盤點輪次:" | |||||
| val dbData = stockTakeVarianceReportService.searchStockTakeVarianceReportV2( | |||||
| stockTakeRoundId = stockTakeRoundId, | |||||
| itemCode = itemCode, | |||||
| ) | |||||
| val stockTakeDateDisplay = dbData | |||||
| .mapNotNull { it["stockTakeDate"] as? String } | |||||
| .filter { it.isNotBlank() } | |||||
| .maxOrNull() | |||||
| ?: "" | |||||
| parameters["stockTakeDate"] = stockTakeDateDisplay | |||||
| val pdfBytes = reportService.createPdfResponse( | val pdfBytes = reportService.createPdfResponse( | ||||
| "/jasper/StockTakeVarianceReport.jrxml", | "/jasper/StockTakeVarianceReport.jrxml", | ||||
| parameters, | parameters, | ||||
| @@ -43,7 +43,7 @@ open class StockTake: BaseEntity<Long>() { | |||||
| @Column(name = "stockTakeSection", length = 255) | @Column(name = "stockTakeSection", length = 255) | ||||
| open var stockTakeSection: String? = null | open var stockTakeSection: String? = null | ||||
| /** 同一輪盤點(多 section 多筆 stock_take)共用此 id,通常等於該輪第一筆 stock_take 的主鍵 */ | |||||
| /** 同一輪盤點(多 section 多筆 stock_take)共用此 id;由批次建立時依 MAX+1 遞增,不必等於任一筆主鍵 */ | |||||
| @Column(name = "stockTakeRoundId") | @Column(name = "stockTakeRoundId") | ||||
| open var stockTakeRoundId: Long? = null | open var stockTakeRoundId: Long? = null | ||||
| } | } | ||||
| @@ -15,4 +15,10 @@ interface StockTakeRepository : AbstractRepository<StockTake, Long> { | |||||
| select st.code from StockTake st where st.code like :prefix% order by st.code desc limit 1 | select st.code from StockTake st where st.code like :prefix% order by st.code desc limit 1 | ||||
| """) | """) | ||||
| fun findLatestCodeByPrefix(prefix: String): String? | fun findLatestCodeByPrefix(prefix: String): String? | ||||
| /** 未刪除列中 stockTakeRoundId 的最大值;全為 null 或無資料時為 0 */ | |||||
| @Query( | |||||
| "SELECT COALESCE(MAX(st.stockTakeRoundId), 0) FROM StockTake st WHERE st.deleted = false" | |||||
| ) | |||||
| fun findMaxStockTakeRoundId(): Long | |||||
| } | } | ||||
| @@ -247,53 +247,12 @@ class StockTakeService( | |||||
| .mapNotNull { it.stockTakeSection } | .mapNotNull { it.stockTakeSection } | ||||
| .distinct() | .distinct() | ||||
| .filter { !it.isBlank() } | .filter { !it.isBlank() } | ||||
| // 2. 获取所有 stock_take 记录(按 stockTakeSection 分组) | |||||
| val allStockTakes = stockTakeRepository.findAll() | |||||
| .filter { !it.deleted } | |||||
| .groupBy { it.stockTakeSection } | |||||
| /* | |||||
| // 3. 为每个 stockTakeSection 检查并创建 | |||||
| distinctSections.forEach { section -> | |||||
| val stockTakesForSection = allStockTakes[section] ?: emptyList() | |||||
| // 检查:如果该 section 的所有记录都是 COMPLETED,才创建新的 | |||||
| val allCompleted = stockTakesForSection.isEmpty() || | |||||
| stockTakesForSection.all { it.status == StockTakeStatus.COMPLETED } | |||||
| if (allCompleted) { | |||||
| try { | |||||
| val now = LocalDateTime.now() | |||||
| val code = assignStockTakeNo() | |||||
| val saveStockTakeReq = SaveStockTakeRequest( | |||||
| code = code, | |||||
| planStart = now, | |||||
| planEnd = now.plusDays(1), | |||||
| actualStart = null, | |||||
| actualEnd = null, | |||||
| status = StockTakeStatus.PENDING.value, | |||||
| remarks = null, | |||||
| stockTakeSection = section | |||||
| ) | |||||
| val savedStockTake = saveStockTake(saveStockTakeReq) | |||||
| result[section] = "Created: ${savedStockTake.code}" | |||||
| logger.info("Created stock take for section $section: ${savedStockTake.code}") | |||||
| } catch (e: Exception) { | |||||
| result[section] = "Error: ${e.message}" | |||||
| logger.error("Error creating stock take for section $section: ${e.message}") | |||||
| } | |||||
| } else { | |||||
| result[section] = "Skipped: Has non-completed records" | |||||
| logger.info("Skipped section $section: Has non-completed records") | |||||
| } | |||||
| } | |||||
| */ | |||||
| // 移除 null section 处理逻辑,因为 warehouse 表中没有 null 的 stockTakeSection | // 移除 null section 处理逻辑,因为 warehouse 表中没有 null 的 stockTakeSection | ||||
| val batchPlanStart = LocalDateTime.now() | val batchPlanStart = LocalDateTime.now() | ||||
| val batchPlanEnd = batchPlanStart.plusDays(1) | val batchPlanEnd = batchPlanStart.plusDays(1) | ||||
| var roundId: Long? = null | |||||
| // 輪次序號:每批次共用同一個遞增 roundId,與各筆 stock_take 主鍵脫鉤(避免第二輪變成 4,4,4) | |||||
| val roundId = stockTakeRepository.findMaxStockTakeRoundId() + 1 | |||||
| distinctSections.forEach { section -> | distinctSections.forEach { section -> | ||||
| try { | try { | ||||
| val code = assignStockTakeNo() | val code = assignStockTakeNo() | ||||
| @@ -309,11 +268,6 @@ class StockTakeService( | |||||
| stockTakeRoundId = roundId | stockTakeRoundId = roundId | ||||
| ) | ) | ||||
| val savedStockTake = saveStockTake(saveStockTakeReq) | val savedStockTake = saveStockTake(saveStockTakeReq) | ||||
| if (roundId == null) { | |||||
| roundId = savedStockTake.id | |||||
| savedStockTake.stockTakeRoundId = roundId | |||||
| stockTakeRepository.save(savedStockTake) | |||||
| } | |||||
| result[section] = "Created: ${savedStockTake.code}" | result[section] = "Created: ${savedStockTake.code}" | ||||
| logger.info("Created stock take for section $section: ${savedStockTake.code}, roundId=$roundId") | logger.info("Created stock take for section $section: ${savedStockTake.code}, roundId=$roundId") | ||||
| } catch (e: Exception) { | } catch (e: Exception) { | ||||
| @@ -35,6 +35,12 @@ | |||||
| <parameter name="lastOutDateEnd" class="java.lang.String"> | <parameter name="lastOutDateEnd" class="java.lang.String"> | ||||
| <parameterDescription><![CDATA["lastOutDateStart"]]></parameterDescription> | <parameterDescription><![CDATA["lastOutDateStart"]]></parameterDescription> | ||||
| </parameter> | </parameter> | ||||
| <parameter name="stockTakeFilterCaption" class="java.lang.String"> | |||||
| <defaultValueExpression><![CDATA[""]]></defaultValueExpression> | |||||
| </parameter> | |||||
| <parameter name="stockTakeConditionLabel" class="java.lang.String"> | |||||
| <defaultValueExpression><![CDATA["盤點日期:"]]></defaultValueExpression> | |||||
| </parameter> | |||||
| <queryString> | <queryString> | ||||
| <![CDATA[]]> | <![CDATA[]]> | ||||
| </queryString> | </queryString> | ||||
| @@ -164,7 +170,7 @@ | |||||
| <textElement textAlignment="Left" verticalAlignment="Middle"> | <textElement textAlignment="Left" verticalAlignment="Middle"> | ||||
| <font fontName="微軟正黑體" size="12" isBold="true"/> | <font fontName="微軟正黑體" size="12" isBold="true"/> | ||||
| </textElement> | </textElement> | ||||
| <textFieldExpression><![CDATA[$P{stockTakeDateStart}+" 至 "+$P{stockTakeDateEnd}]]></textFieldExpression> | |||||
| <textFieldExpression><![CDATA[($P{stockTakeFilterCaption} != null && !$P{stockTakeFilterCaption}.isEmpty()) ? $P{stockTakeFilterCaption} : ($P{stockTakeDateStart} + " 至 " + $P{stockTakeDateEnd})]]></textFieldExpression> | |||||
| </textField> | </textField> | ||||
| <staticText> | <staticText> | ||||
| <reportElement x="747" y="0" width="21" height="23" uuid="5a1b4b58-b7b1-48c9-b229-7e96392c6425"> | <reportElement x="747" y="0" width="21" height="23" uuid="5a1b4b58-b7b1-48c9-b229-7e96392c6425"> | ||||
| @@ -184,16 +190,16 @@ | |||||
| <textElement textAlignment="Center" verticalAlignment="Middle"/> | <textElement textAlignment="Center" verticalAlignment="Middle"/> | ||||
| <textFieldExpression><![CDATA[$V{PAGE_NUMBER}]]></textFieldExpression> | <textFieldExpression><![CDATA[$V{PAGE_NUMBER}]]></textFieldExpression> | ||||
| </textField> | </textField> | ||||
| <staticText> | |||||
| <reportElement x="510" y="30" width="68" height="23" uuid="3741c716-b76a-442a-a4f8-0152853941d5"> | |||||
| <textField> | |||||
| <reportElement x="510" y="30" width="80" height="23" uuid="3741c716-b76a-442a-a4f8-0152853941d5"> | |||||
| <property name="com.jaspersoft.studio.unit.y" value="px"/> | <property name="com.jaspersoft.studio.unit.y" value="px"/> | ||||
| <property name="com.jaspersoft.studio.unit.height" value="px"/> | <property name="com.jaspersoft.studio.unit.height" value="px"/> | ||||
| </reportElement> | </reportElement> | ||||
| <textElement textAlignment="Left" verticalAlignment="Middle"> | <textElement textAlignment="Left" verticalAlignment="Middle"> | ||||
| <font fontName="微軟正黑體" size="12" isBold="true"/> | <font fontName="微軟正黑體" size="12" isBold="true"/> | ||||
| </textElement> | </textElement> | ||||
| <text><![CDATA[盤點日期:]]></text> | |||||
| </staticText> | |||||
| <textFieldExpression><![CDATA[$P{stockTakeConditionLabel}]]></textFieldExpression> | |||||
| </textField> | |||||
| <staticText> | <staticText> | ||||
| <reportElement x="10" y="30" width="90" height="23" uuid="0628b331-5736-4f93-bed3-2278011456aa"> | <reportElement x="10" y="30" width="90" height="23" uuid="0628b331-5736-4f93-bed3-2278011456aa"> | ||||
| <property name="com.jaspersoft.studio.unit.y" value="px"/> | <property name="com.jaspersoft.studio.unit.y" value="px"/> | ||||
| @@ -249,7 +255,7 @@ | |||||
| <textElement textAlignment="Right" verticalAlignment="Middle"> | <textElement textAlignment="Right" verticalAlignment="Middle"> | ||||
| <font fontName="微軟正黑體" size="10"/> | <font fontName="微軟正黑體" size="10"/> | ||||
| </textElement> | </textElement> | ||||
| <text><![CDATA[現存存量]]></text> | |||||
| <text><![CDATA[盤點前存量]]></text> | |||||
| </staticText> | </staticText> | ||||
| <staticText> | <staticText> | ||||
| <reportElement isPrintRepeatedValues="false" x="228" y="1" width="110" height="18" isPrintInFirstWholeBand="true" uuid="e95a755d-4ecb-4900-ac9a-3a6e3b9b3470"> | <reportElement isPrintRepeatedValues="false" x="228" y="1" width="110" height="18" isPrintInFirstWholeBand="true" uuid="e95a755d-4ecb-4900-ac9a-3a6e3b9b3470"> | ||||
| @@ -271,7 +277,7 @@ | |||||
| <textElement textAlignment="Center" verticalAlignment="Middle"> | <textElement textAlignment="Center" verticalAlignment="Middle"> | ||||
| <font fontName="微軟正黑體" size="10"/> | <font fontName="微軟正黑體" size="10"/> | ||||
| </textElement> | </textElement> | ||||
| <text><![CDATA[最後出入倉日期]]></text> | |||||
| <text><![CDATA[審核時間]]></text> | |||||
| </staticText> | </staticText> | ||||
| <staticText> | <staticText> | ||||
| <reportElement isPrintRepeatedValues="false" x="558" y="1" width="100" height="18" isPrintInFirstWholeBand="true" uuid="921c16b3-172b-43b9-a090-24d2ee88a1b2"> | <reportElement isPrintRepeatedValues="false" x="558" y="1" width="100" height="18" isPrintInFirstWholeBand="true" uuid="921c16b3-172b-43b9-a090-24d2ee88a1b2"> | ||||