Przeglądaj źródła

stock take update

production
CANCERYS\kw093 3 tygodni temu
rodzic
commit
2383b62ad0
5 zmienionych plików z 445 dodań i 28 usunięć
  1. +5
    -1
      src/main/java/com/ffii/fpsms/modules/master/web/models/SaveWarehouseRequest.kt
  2. +5
    -1
      src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt
  3. +303
    -8
      src/main/java/com/ffii/fpsms/modules/report/service/StockTakeVarianceReportService.kt
  4. +78
    -18
      src/main/java/com/ffii/fpsms/modules/report/web/StockTakeVarianceReportController.kt
  5. +54
    -0
      src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt

+ 5
- 1
src/main/java/com/ffii/fpsms/modules/master/web/models/SaveWarehouseRequest.kt Wyświetl plik

@@ -41,7 +41,11 @@ data class NewWarehouseRequest(
data class StockTakeSectionInfo(
val stockTakeSection: String,
val stockTakeSectionDescription: String?,
val warehouseCount: Long
val warehouseCount: Long,
/** 該盤點區域所屬樓層/店別(取自倉庫 `store_id`,多筆時取第一筆非空) */
val storeId: String? = null,
/** 倉庫 `area`(多筆時取第一筆非空),對應列表卡片上的區域欄位 */
val warehouseArea: String? = null,
)




+ 5
- 1
src/main/java/com/ffii/fpsms/modules/report/service/ReportService.kt Wyświetl plik

@@ -279,7 +279,11 @@ return result
SELECT
CAST(st.stockTakeRoundId AS CHAR) AS value,
CONCAT(
CAST(st.stockTakeRoundId AS CHAR),
CASE
WHEN NULLIF(TRIM(MAX(st.stockTakeRoundName)), '') IS NULL
THEN CONCAT('盤點輪次', CAST(st.stockTakeRoundId AS CHAR))
ELSE TRIM(MAX(st.stockTakeRoundName))
END,
' — ',
DATE_FORMAT(MIN(st.planStart), '%Y-%m-%d')
) AS label


+ 303
- 8
src/main/java/com/ffii/fpsms/modules/report/service/StockTakeVarianceReportService.kt Wyświetl plik

@@ -565,16 +565,311 @@ ORDER BY
return jdbcDao.queryForList(sql, args)
}

/**
* V2 Overall:每個 (lotId, warehouseId) 取 **status=completed** 且 **stockTakeRoundId 最大** 之一筆盤點紀錄
* (同輪多筆時取 id 較大者)。期初/累計區間依該筆所屬輪次之 MIN~MAX(`date`)。
* @param limitedToRoundIds null=全系統所有輪次;非 null=僅在指定輪次內取最新已完成
*/
fun searchStockTakeVarianceReportV2Overall(
itemCode: String?,
storeId: String?,
limitedToRoundIds: List<Long>? = null,
): List<Map<String, Any>> {
val args = mutableMapOf<String, Any>()

val roundIdsFilterSql = if (limitedToRoundIds.isNullOrEmpty()) {
""
} else {
val inClause = limitedToRoundIds.mapIndexed { index, id ->
val key = "overallRoundId_$index"
args[key] = id
":$key"
}.joinToString(", ")
"AND s.stockTakeRoundId IN ($inClause)"
}

val storeIdSql = run {
val normalized = storeId?.trim()
if (normalized.isNullOrBlank() || normalized.equals("all", ignoreCase = true)) {
""
} else {
args["storeId"] = normalized
"""
AND REPLACE(COALESCE(wh.store_id, ''), '/', '') = REPLACE(:storeId, '/', '')
""".trimIndent()
}
}
val itemCodeSql = buildMultiValueLikeClause(
itemCode,
"it.code",
"itemCode",
args
)

val sql = """
WITH round_bounds AS (
SELECT
s.stockTakeRoundId,
COALESCE(MIN(s.date), CURRENT_DATE) AS fromDate,
COALESCE(MAX(s.date), CURRENT_DATE) AS toDate
FROM stocktakerecord s
WHERE s.deleted = 0
$roundIdsFilterSql
GROUP BY s.stockTakeRoundId
),
latest_str AS (
SELECT
str.lotId,
str.warehouseId,
str.stockTakeRoundId,
str.bookQty,
str.varianceQty,
str.approverStockTakeQty,
str.date AS strDate,
str.id,
str.approverTime,
str.status AS stockTakeRecordStatus
FROM stocktakerecord str
INNER JOIN (
SELECT
s.lotId,
s.warehouseId,
MAX(s.stockTakeRoundId) AS maxRound
FROM stocktakerecord s
WHERE s.deleted = 0
AND s.status = 'completed'
$roundIdsFilterSql
GROUP BY s.lotId, s.warehouseId
) mx ON mx.lotId = str.lotId
AND mx.warehouseId = str.warehouseId
AND mx.maxRound = str.stockTakeRoundId
WHERE str.deleted = 0
AND str.status = 'completed'
AND NOT EXISTS (
SELECT 1
FROM stocktakerecord str2
WHERE str2.deleted = 0
AND str2.status = 'completed'
AND str2.lotId = str.lotId
AND str2.warehouseId = str.warehouseId
AND str2.stockTakeRoundId = str.stockTakeRoundId
AND str2.id > str.id
)
),
line_bounds AS (
SELECT
ill.id AS inventoryLotLineId,
ls.lotId,
ls.warehouseId,
rb.fromDate,
rb.toDate,
ls.bookQty,
ls.varianceQty,
ls.approverStockTakeQty,
ls.strDate,
ls.approverTime,
ls.stockTakeRecordStatus
FROM latest_str ls
INNER JOIN round_bounds rb ON rb.stockTakeRoundId = ls.stockTakeRoundId
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
),
in_agg AS (
SELECT
lb.inventoryLotLineId,
SUM(CASE WHEN DATE(sil.receiptDate) < lb.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 lb.fromDate AND lb.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 line_bounds lb
INNER JOIN inventory_lot_line ill ON ill.id = lb.inventoryLotLineId
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 lb.inventoryLotLineId
),
out_agg AS (
SELECT
lb.inventoryLotLineId,
SUM(CASE WHEN DATE(sol.endTime) < lb.fromDate THEN COALESCE(sol.qty, 0) ELSE 0 END) AS outBefore,
SUM(CASE WHEN DATE(sol.endTime) BETWEEN lb.fromDate AND lb.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 line_bounds lb
INNER JOIN inventory_lot_line ill ON ill.id = lb.inventoryLotLineId
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 lb.inventoryLotLineId
),
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,

lb.bookQty AS stkBookQty,
lb.approverStockTakeQty AS stkApproverQty,
lb.varianceQty AS stkVarianceQty,
lb.strDate AS stockTakeDateRaw,
lb.approverTime AS approvalDateTimeRaw,
lb.stockTakeRecordStatus AS stockTakeRecordStatus
FROM line_bounds lb
INNER JOIN inventory_lot_line ill ON ill.id = lb.inventoryLotLineId
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
INNER JOIN warehouse wh ON wh.id = lb.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
$storeIdSql
)

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 COALESCE(stockTakeRecordStatus, '') <> 'completed' THEN '0'
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 COALESCE(stockTakeRecordStatus, '') <> 'completed' THEN '0%'
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,
CASE WHEN SUM(COALESCE(stkApproverQty, 0)) OVER (PARTITION BY itemNo) < 0 THEN CONCAT('(', FORMAT(-SUM(COALESCE(stkApproverQty, 0)) OVER (PARTITION BY itemNo), 0), ')') ELSE FORMAT(SUM(COALESCE(stkApproverQty, 0)) OVER (PARTITION BY itemNo), 0) END AS totalStockTakeQty

FROM data
ORDER BY
itemNo,
lotNo,
storeLocation
""".trimIndent()

return jdbcDao.queryForList(sql, args)
}

/** Overall 模式報表副標題(全系統輪次,舊 API 相容) */
fun getStockTakeVarianceOverallCaption(): String =
"全輪次(各批號/倉別最新已完成盤點)"

/** 多選輪次 Overall 模式報表副標題 */
fun getStockTakeVarianceMultiRoundCaption(stockTakeRoundIds: List<Long>): String {
if (stockTakeRoundIds.isEmpty()) return getStockTakeVarianceOverallCaption()
val parts = stockTakeRoundIds.map { getStockTakeRoundCaption(it) }
return "已選輪次(各批號/倉別最新已完成盤點):${parts.joinToString(";")}"
}

/** 報表表頭:盤點輪次說明(與 /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
SELECT
CONCAT(
CASE
WHEN NULLIF(TRIM(MAX(st.stockTakeRoundName)), '') IS NULL
THEN CONCAT('盤點輪次', CAST(st.stockTakeRoundId AS CHAR))
ELSE TRIM(MAX(st.stockTakeRoundName))
END,
' — ',
DATE_FORMAT(MIN(st.planStart), '%Y-%m-%d')
) AS cap
FROM stock_take st
WHERE st.deleted = 0
AND st.stockTakeRoundId = :stockTakeRoundId
@@ -585,7 +880,7 @@ ORDER BY
mapOf("stockTakeRoundId" to stockTakeRoundId)
).firstOrNull()
val cap = row?.get("cap") as? String
return if (!cap.isNullOrBlank()) cap else "Round $stockTakeRoundId"
return if (!cap.isNullOrBlank()) cap else "盤點輪次$stockTakeRoundId"
}

/** LIKE 多值工具方法 */


+ 78
- 18
src/main/java/com/ffii/fpsms/modules/report/web/StockTakeVarianceReportController.kt Wyświetl plik

@@ -141,11 +141,14 @@ class StockTakeVarianceReportController(
*/
@GetMapping("/print-stock-take-variance-v2")
fun generateStockTakeVarianceReportV2(
@RequestParam stockTakeRoundId: Long,
@RequestParam(required = false) overall: Boolean?,
@RequestParam(required = false) stockTakeRoundId: String?,
@RequestParam(required = false) itemCode: String?,
@RequestParam(required = false, name = "store_id") storeId: String?,
@RequestParam(required = false) status: String?,
): ResponseEntity<ByteArray> {
val rep012 = resolveRep012ReportData(overall, stockTakeRoundId, itemCode, storeId, status)

val parameters = mutableMapOf<String, Any>()

parameters["stockCategory"] = "All"
@@ -164,16 +167,10 @@ class StockTakeVarianceReportController(
parameters["lastInDateEnd"] = ""
parameters["lastOutDateEnd"] = ""

parameters["stockTakeFilterCaption"] =
stockTakeVarianceReportService.getStockTakeRoundCaption(stockTakeRoundId)
parameters["stockTakeConditionLabel"] = "盤點輪次:"
parameters["stockTakeFilterCaption"] = rep012.caption
parameters["stockTakeConditionLabel"] = rep012.conditionLabel

val dbData = stockTakeVarianceReportService.searchStockTakeVarianceReportV2(
stockTakeRoundId = stockTakeRoundId,
itemCode = itemCode,
storeId = storeId,
status = status,
)
val dbData = rep012.dbData
val stockTakeDateDisplay = dbData
.mapNotNull { it["stockTakeDate"] as? String }
.filter { it.isNotBlank() }
@@ -198,18 +195,15 @@ class StockTakeVarianceReportController(

@GetMapping("/print-stock-take-variance-v2-excel")
fun exportStockTakeVarianceReportV2Excel(
@RequestParam stockTakeRoundId: Long,
@RequestParam(required = false) overall: Boolean?,
@RequestParam(required = false) stockTakeRoundId: String?,
@RequestParam(required = false) itemCode: String?,
@RequestParam(required = false, name = "store_id") storeId: String?,
@RequestParam(required = false) status: String?,
): ResponseEntity<ByteArray> {
val cap = stockTakeVarianceReportService.getStockTakeRoundCaption(stockTakeRoundId)
val dbData = stockTakeVarianceReportService.searchStockTakeVarianceReportV2(
stockTakeRoundId = stockTakeRoundId,
itemCode = itemCode,
storeId = storeId,
status = status,
)
val rep012 = resolveRep012ReportData(overall, stockTakeRoundId, itemCode, storeId, status)
val cap = rep012.caption
val dbData = rep012.dbData

val excelBytes = createStockTakeVarianceExcel(
dbData = dbData,
@@ -579,5 +573,71 @@ class StockTakeVarianceReportController(
val abs = kotlin.math.abs(v).toLong()
return "%,d".format(abs)
}

private data class Rep012ReportData(
val caption: String,
val conditionLabel: String,
val dbData: List<Map<String, Any>>,
)

private fun parseStockTakeRoundIds(raw: String?): List<Long> =
raw
?.split(",")
?.mapNotNull { it.trim().toLongOrNull() }
?.distinct()
?: emptyList()

/**
* rep-012:單輪=單輪查詢;多輪=已選輪次內各批號/倉最新已完成(status 固定 completed)。
* overall=true 且未傳輪次時保留舊「全系統輪次」行為。
*/
private fun resolveRep012ReportData(
overall: Boolean?,
stockTakeRoundId: String?,
itemCode: String?,
storeId: String?,
status: String?,
): Rep012ReportData {
val roundIds = parseStockTakeRoundIds(stockTakeRoundId)
val useLegacyOverall = overall == true && roundIds.isEmpty()
require(useLegacyOverall || roundIds.isNotEmpty()) {
"stockTakeRoundId is required"
}

if (useLegacyOverall) {
return Rep012ReportData(
caption = stockTakeVarianceReportService.getStockTakeVarianceOverallCaption(),
conditionLabel = "盤點條件:",
dbData = stockTakeVarianceReportService.searchStockTakeVarianceReportV2Overall(
itemCode = itemCode,
storeId = storeId,
),
)
}

if (roundIds.size == 1) {
val id = roundIds.first()
return Rep012ReportData(
caption = stockTakeVarianceReportService.getStockTakeRoundCaption(id),
conditionLabel = "盤點輪次:",
dbData = stockTakeVarianceReportService.searchStockTakeVarianceReportV2(
stockTakeRoundId = id,
itemCode = itemCode,
storeId = storeId,
status = status,
),
)
}

return Rep012ReportData(
caption = stockTakeVarianceReportService.getStockTakeVarianceMultiRoundCaption(roundIds),
conditionLabel = "盤點條件:",
dbData = stockTakeVarianceReportService.searchStockTakeVarianceReportV2Overall(
itemCode = itemCode,
storeId = storeId,
limitedToRoundIds = roundIds,
),
)
}
}


+ 54
- 0
src/main/java/com/ffii/fpsms/modules/stock/entity/StockLedgerRepository.kt Wyświetl plik

@@ -6,6 +6,7 @@ import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository
import java.time.LocalDate
import java.time.LocalDateTime
@Repository
interface StockLedgerRepository: AbstractRepository<StockLedger, Long> {
@@ -59,4 +60,57 @@ interface StockLedgerRepository: AbstractRepository<StockLedger, Long> {
""")
fun findLatestByItemId(@Param("itemId") itemId: Long): List<StockLedger>
fun findFirstByItemIdAndDeletedFalseOrderByDateDescIdDesc(itemId: Long): StockLedger?

@Query("""
SELECT sl FROM StockLedger sl
LEFT JOIN sl.stockOutLine sol
LEFT JOIN sol.inventoryLotLine ill
LEFT JOIN ill.inventoryLot il
LEFT JOIN sl.inventory inv
LEFT JOIN inv.item i
WHERE sl.deleted = false
AND sl.type = :type
AND sl.outQty IS NOT NULL AND sl.outQty > 0
AND (:itemCode IS NULL OR sl.itemCode LIKE CONCAT('%', :itemCode, '%'))
AND (:itemName IS NULL OR i.name LIKE CONCAT('%', :itemName, '%'))
AND (:lotNo IS NULL OR il.lotNo LIKE CONCAT('%', :lotNo, '%'))
AND (:startDate IS NULL OR sl.date >= :startDate)
AND (:endDateExclusive IS NULL OR sl.date < :endDateExclusive)
ORDER BY sl.date DESC, sl.id DESC
""")
fun findStockIssueHandleRecords(
@Param("type") type: String,
@Param("itemCode") itemCode: String?,
@Param("itemName") itemName: String?,
@Param("lotNo") lotNo: String?,
@Param("startDate") startDate: LocalDate?,
@Param("endDateExclusive") endDateExclusive: LocalDate?,
pageable: Pageable,
): Page<StockLedger>

@Query("""
SELECT sl FROM StockLedger sl
LEFT JOIN sl.stockOutLine sol
LEFT JOIN sol.inventoryLotLine ill
LEFT JOIN ill.inventoryLot il
LEFT JOIN sl.inventory inv
LEFT JOIN inv.item i
WHERE sl.deleted = false
AND sl.type = 'Expiry'
AND sl.outQty IS NOT NULL AND sl.outQty > 0
AND (:itemCode IS NULL OR sl.itemCode LIKE CONCAT('%', :itemCode, '%'))
AND (:itemName IS NULL OR i.name LIKE CONCAT('%', :itemName, '%'))
AND (:lotNo IS NULL OR il.lotNo LIKE CONCAT('%', :lotNo, '%'))
AND (:startDate IS NULL OR il.expiryDate >= :startDate)
AND (:endDateExclusive IS NULL OR il.expiryDate < :endDateExclusive)
ORDER BY sl.date DESC, sl.id DESC
""")
fun findExpiryItemHandleRecords(
@Param("itemCode") itemCode: String?,
@Param("itemName") itemName: String?,
@Param("lotNo") lotNo: String?,
@Param("startDate") startDate: LocalDate?,
@Param("endDateExclusive") endDateExclusive: LocalDate?,
pageable: Pageable,
): Page<StockLedger>
}

Ładowanie…
Anuluj
Zapisz