|
|
@@ -766,12 +766,11 @@ fun searchMaterialStockOutTraceabilityReport( |
|
|
return jdbcDao.queryForList(sql, args) |
|
|
return jdbcDao.queryForList(sql, args) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Queries the database for Stock Balance Report data. |
|
|
* Queries the database for Stock Balance Report data. |
|
|
* Shows stock balances by item code and lot number, including opening balance, |
|
|
|
|
|
* cumulative stock in/out, current balance, store locations, and last in/out dates. |
|
|
|
|
|
* Opening balance comes from stock_ledger where type = 'adj'. |
|
|
|
|
|
|
|
|
* Shows stock balances by item code and lot number (per-lot quantities from inventory_lot_line), |
|
|
|
|
|
* including opening balance (item-level from stock_ledger type='adj'), cumulative stock in/out, |
|
|
|
|
|
* current balance, store locations, and last in/out dates per lot. |
|
|
*/ |
|
|
*/ |
|
|
fun searchStockBalanceReport( |
|
|
fun searchStockBalanceReport( |
|
|
stockCategory: String?, |
|
|
stockCategory: String?, |
|
|
@@ -802,19 +801,19 @@ fun searchMaterialStockOutTraceabilityReport( |
|
|
COALESCE(uc.code, '') as unitOfMeasure, |
|
|
COALESCE(uc.code, '') as unitOfMeasure, |
|
|
COALESCE(il.lotNo, sil.lotNo, '') as lotNo, |
|
|
COALESCE(il.lotNo, sil.lotNo, '') as lotNo, |
|
|
COALESCE(DATE_FORMAT(COALESCE(il.expiryDate, sil.expiryDate), '%Y-%m-%d'), '') as expiryDate, |
|
|
COALESCE(DATE_FORMAT(COALESCE(il.expiryDate, sil.expiryDate), '%Y-%m-%d'), '') as expiryDate, |
|
|
FORMAT(ROUND(COALESCE(opening_bal.openingBalance, 0), 0), 0) as openingBalance, |
|
|
|
|
|
FORMAT(ROUND(COALESCE(cum_in.cumStockIn, 0), 0), 0) as cumStockIn, |
|
|
|
|
|
FORMAT(ROUND(COALESCE(cum_out.cumStockOut, 0), 0), 0) as cumStockOut, |
|
|
|
|
|
FORMAT(ROUND(COALESCE(opening_bal.openingBalance, 0) + COALESCE(cum_in.cumStockIn, 0) - COALESCE(cum_out.cumStockOut, 0), 0), 0) as currentBalance, |
|
|
|
|
|
|
|
|
FORMAT(ROUND(COALESCE(lot_agg.lotOpening, 0), 0), 0) as openingBalance, |
|
|
|
|
|
FORMAT(ROUND(COALESCE(lot_agg.lotCumIn, 0), 0), 0) as cumStockIn, |
|
|
|
|
|
FORMAT(ROUND(COALESCE(lot_agg.lotCumOut, 0), 0), 0) as cumStockOut, |
|
|
|
|
|
FORMAT(ROUND(COALESCE(lot_agg.lotOpening, 0) + COALESCE(lot_agg.lotCumIn, 0) - COALESCE(lot_agg.lotCumOut, 0), 0), 0) as currentBalance, |
|
|
'' as reOrderLevel, |
|
|
'' as reOrderLevel, |
|
|
'' as reOrderQty, |
|
|
'' as reOrderQty, |
|
|
COALESCE(GROUP_CONCAT(DISTINCT wh.code ORDER BY wh.code SEPARATOR ', '), '') as storeLocation, |
|
|
COALESCE(GROUP_CONCAT(DISTINCT wh.code ORDER BY wh.code SEPARATOR ', '), '') as storeLocation, |
|
|
COALESCE(DATE_FORMAT(cum_in.lastInDate, '%Y-%m-%d'), '') as lastInDate, |
|
|
|
|
|
COALESCE(DATE_FORMAT(cum_out.lastOutDate, '%Y-%m-%d'), '') as lastOutDate, |
|
|
|
|
|
FORMAT(ROUND(SUM(COALESCE(opening_bal.openingBalance, 0)) OVER (PARTITION BY it.code), 0), 0) as totalOpeningBalance, |
|
|
|
|
|
FORMAT(ROUND(SUM(COALESCE(cum_in.cumStockIn, 0)) OVER (PARTITION BY it.code), 0), 0) as totalCumStockIn, |
|
|
|
|
|
FORMAT(ROUND(SUM(COALESCE(cum_out.cumStockOut, 0)) OVER (PARTITION BY it.code), 0), 0) as totalCumStockOut, |
|
|
|
|
|
FORMAT(ROUND(SUM(COALESCE(opening_bal.openingBalance, 0) + COALESCE(cum_in.cumStockIn, 0) - COALESCE(cum_out.cumStockOut, 0)) OVER (PARTITION BY it.code), 0), 0) as totalCurrentBalance |
|
|
|
|
|
|
|
|
COALESCE(DATE_FORMAT(lot_agg.lotLastInDate, '%Y-%m-%d'), '') as lastInDate, |
|
|
|
|
|
COALESCE(DATE_FORMAT(lot_agg.lotLastOutDate, '%Y-%m-%d'), '') as lastOutDate, |
|
|
|
|
|
FORMAT(ROUND(MAX(COALESCE(opening_bal.openingBalance, 0)) OVER (PARTITION BY it.code), 0), 0) as totalOpeningBalance, |
|
|
|
|
|
FORMAT(ROUND(SUM(COALESCE(lot_agg.lotCumIn, 0)) OVER (PARTITION BY it.code), 0), 0) as totalCumStockIn, |
|
|
|
|
|
FORMAT(ROUND(SUM(COALESCE(lot_agg.lotCumOut, 0)) OVER (PARTITION BY it.code), 0), 0) as totalCumStockOut, |
|
|
|
|
|
FORMAT(ROUND(SUM(COALESCE(lot_agg.lotOpening, 0) + COALESCE(lot_agg.lotCumIn, 0) - COALESCE(lot_agg.lotCumOut, 0)) OVER (PARTITION BY it.code), 0), 0) as totalCurrentBalance |
|
|
FROM inventory_lot il |
|
|
FROM inventory_lot il |
|
|
LEFT JOIN items it ON il.itemId = it.id AND it.deleted = false |
|
|
LEFT JOIN items it ON il.itemId = it.id AND it.deleted = false |
|
|
LEFT JOIN stock_in_line sil ON il.stockInLineId = sil.id AND sil.deleted = false |
|
|
LEFT JOIN stock_in_line sil ON il.stockInLineId = sil.id AND sil.deleted = false |
|
|
@@ -824,84 +823,85 @@ fun searchMaterialStockOutTraceabilityReport( |
|
|
LEFT JOIN uom_conversion uc ON iu.uomId = uc.id |
|
|
LEFT JOIN uom_conversion uc ON iu.uomId = uc.id |
|
|
LEFT JOIN ( |
|
|
LEFT JOIN ( |
|
|
SELECT |
|
|
SELECT |
|
|
sl.itemCode, |
|
|
|
|
|
SUM(COALESCE(sl.balance, 0)) as openingBalance |
|
|
|
|
|
FROM stock_ledger sl |
|
|
|
|
|
WHERE sl.deleted = false |
|
|
|
|
|
AND sl.type = 'adj' |
|
|
|
|
|
AND sl.itemCode IS NOT NULL |
|
|
|
|
|
AND sl.itemCode != '' |
|
|
|
|
|
GROUP BY sl.itemCode |
|
|
|
|
|
) opening_bal ON it.code = opening_bal.itemCode |
|
|
|
|
|
|
|
|
ill_agg.inventoryLotId, |
|
|
|
|
|
SUM(COALESCE(ill_agg.inQty, 0)) as lotCumIn, |
|
|
|
|
|
SUM(COALESCE(ill_agg.outQty, 0)) as lotCumOut, |
|
|
|
|
|
NULL as lotOpening, |
|
|
|
|
|
last_in.lotLastInDate, |
|
|
|
|
|
last_out.lotLastOutDate |
|
|
|
|
|
FROM inventory_lot_line ill_agg |
|
|
|
|
|
LEFT JOIN ( |
|
|
|
|
|
SELECT sil2.inventoryLotId, MAX(sil2.receiptDate) as lotLastInDate |
|
|
|
|
|
FROM stock_in_line sil2 |
|
|
|
|
|
WHERE sil2.deleted = false AND sil2.inventoryLotId IS NOT NULL |
|
|
|
|
|
GROUP BY sil2.inventoryLotId |
|
|
|
|
|
) last_in ON last_in.inventoryLotId = ill_agg.inventoryLotId |
|
|
|
|
|
LEFT JOIN ( |
|
|
|
|
|
SELECT ill2.inventoryLotId, MAX(sol.endTime) as lotLastOutDate |
|
|
|
|
|
FROM stock_out_line sol |
|
|
|
|
|
INNER JOIN inventory_lot_line ill2 ON sol.inventoryLotLineId = ill2.id AND ill2.deleted = false |
|
|
|
|
|
WHERE sol.deleted = false |
|
|
|
|
|
GROUP BY ill2.inventoryLotId |
|
|
|
|
|
) last_out ON last_out.inventoryLotId = ill_agg.inventoryLotId |
|
|
|
|
|
WHERE ill_agg.deleted = false |
|
|
|
|
|
GROUP BY ill_agg.inventoryLotId, last_in.lotLastInDate, last_out.lotLastOutDate |
|
|
|
|
|
) lot_agg ON lot_agg.inventoryLotId = il.id |
|
|
LEFT JOIN ( |
|
|
LEFT JOIN ( |
|
|
SELECT |
|
|
SELECT |
|
|
sl.itemCode, |
|
|
sl.itemCode, |
|
|
SUM(COALESCE(sl.inQty, 0)) as cumStockIn, |
|
|
|
|
|
MAX(CASE WHEN sl.inQty > 0 THEN sl.date ELSE NULL END) as lastInDate |
|
|
|
|
|
FROM stock_ledger sl |
|
|
|
|
|
WHERE sl.deleted = false |
|
|
|
|
|
AND sl.itemCode IS NOT NULL |
|
|
|
|
|
AND sl.itemCode != '' |
|
|
|
|
|
AND COALESCE(sl.inQty, 0) > 0 |
|
|
|
|
|
GROUP BY sl.itemCode |
|
|
|
|
|
) cum_in ON it.code = cum_in.itemCode |
|
|
|
|
|
LEFT JOIN ( |
|
|
|
|
|
SELECT |
|
|
|
|
|
sl.itemCode, |
|
|
|
|
|
SUM(COALESCE(sl.outQty, 0)) as cumStockOut, |
|
|
|
|
|
MAX(CASE WHEN sl.outQty > 0 THEN sl.date ELSE NULL END) as lastOutDate |
|
|
|
|
|
|
|
|
SUM(COALESCE(sl.balance, 0)) as openingBalance |
|
|
FROM stock_ledger sl |
|
|
FROM stock_ledger sl |
|
|
WHERE sl.deleted = false |
|
|
WHERE sl.deleted = false |
|
|
|
|
|
AND LOWER(sl.type) = 'adj' |
|
|
AND sl.itemCode IS NOT NULL |
|
|
AND sl.itemCode IS NOT NULL |
|
|
AND sl.itemCode != '' |
|
|
AND sl.itemCode != '' |
|
|
AND COALESCE(sl.outQty, 0) > 0 |
|
|
|
|
|
GROUP BY sl.itemCode |
|
|
GROUP BY sl.itemCode |
|
|
) cum_out ON it.code = cum_out.itemCode |
|
|
|
|
|
|
|
|
) opening_bal ON it.code = opening_bal.itemCode |
|
|
WHERE il.deleted = false |
|
|
WHERE il.deleted = false |
|
|
$stockCategorySql |
|
|
$stockCategorySql |
|
|
$itemCodeSql |
|
|
$itemCodeSql |
|
|
$storeLocationSql |
|
|
$storeLocationSql |
|
|
GROUP BY it.code, it.name, uc.code, il.lotNo, sil.lotNo, il.expiryDate, sil.expiryDate, |
|
|
|
|
|
opening_bal.openingBalance, cum_in.cumStockIn, cum_in.lastInDate, |
|
|
|
|
|
cum_out.cumStockOut, cum_out.lastOutDate |
|
|
|
|
|
|
|
|
GROUP BY it.code, it.name, uc.code, il.id, il.lotNo, sil.lotNo, il.expiryDate, sil.expiryDate, |
|
|
|
|
|
lot_agg.lotCumIn, lot_agg.lotCumOut, lot_agg.lotOpening, lot_agg.lotLastInDate, lot_agg.lotLastOutDate, |
|
|
|
|
|
opening_bal.openingBalance |
|
|
HAVING 1=1 |
|
|
HAVING 1=1 |
|
|
""".trimIndent() |
|
|
""".trimIndent() |
|
|
|
|
|
|
|
|
// Apply filters that need to be in HAVING clause |
|
|
|
|
|
val havingConditions = mutableListOf<String>() |
|
|
val havingConditions = mutableListOf<String>() |
|
|
|
|
|
val lotCurrentBalanceExpr = "(COALESCE(lot_agg.lotOpening, 0) + COALESCE(lot_agg.lotCumIn, 0) - COALESCE(lot_agg.lotCumOut, 0))" |
|
|
|
|
|
|
|
|
if (!balanceFilterStart.isNullOrBlank()) { |
|
|
if (!balanceFilterStart.isNullOrBlank()) { |
|
|
args["balanceFilterStart"] = balanceFilterStart.toDoubleOrNull() ?: 0.0 |
|
|
args["balanceFilterStart"] = balanceFilterStart.toDoubleOrNull() ?: 0.0 |
|
|
havingConditions.add("(COALESCE(opening_bal.openingBalance, 0) + COALESCE(cum_in.cumStockIn, 0) - COALESCE(cum_out.cumStockOut, 0)) >= :balanceFilterStart") |
|
|
|
|
|
|
|
|
havingConditions.add("$lotCurrentBalanceExpr >= :balanceFilterStart") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!balanceFilterEnd.isNullOrBlank()) { |
|
|
if (!balanceFilterEnd.isNullOrBlank()) { |
|
|
args["balanceFilterEnd"] = balanceFilterEnd.toDoubleOrNull() ?: 0.0 |
|
|
args["balanceFilterEnd"] = balanceFilterEnd.toDoubleOrNull() ?: 0.0 |
|
|
havingConditions.add("(COALESCE(opening_bal.openingBalance, 0) + COALESCE(cum_in.cumStockIn, 0) - COALESCE(cum_out.cumStockOut, 0)) <= :balanceFilterEnd") |
|
|
|
|
|
|
|
|
havingConditions.add("$lotCurrentBalanceExpr <= :balanceFilterEnd") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!lastInDateStart.isNullOrBlank()) { |
|
|
if (!lastInDateStart.isNullOrBlank()) { |
|
|
val formattedDate = lastInDateStart.replace("/", "-") |
|
|
val formattedDate = lastInDateStart.replace("/", "-") |
|
|
args["lastInDateStart"] = formattedDate |
|
|
args["lastInDateStart"] = formattedDate |
|
|
havingConditions.add("(cum_in.lastInDate IS NOT NULL AND DATE(cum_in.lastInDate) >= DATE(:lastInDateStart))") |
|
|
|
|
|
|
|
|
havingConditions.add("(lot_agg.lotLastInDate IS NOT NULL AND DATE(lot_agg.lotLastInDate) >= DATE(:lastInDateStart))") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!lastInDateEnd.isNullOrBlank()) { |
|
|
if (!lastInDateEnd.isNullOrBlank()) { |
|
|
val formattedDate = lastInDateEnd.replace("/", "-") |
|
|
val formattedDate = lastInDateEnd.replace("/", "-") |
|
|
args["lastInDateEnd"] = formattedDate |
|
|
args["lastInDateEnd"] = formattedDate |
|
|
havingConditions.add("(cum_in.lastInDate IS NOT NULL AND DATE(cum_in.lastInDate) <= DATE(:lastInDateEnd))") |
|
|
|
|
|
|
|
|
havingConditions.add("(lot_agg.lotLastInDate IS NOT NULL AND DATE(lot_agg.lotLastInDate) <= DATE(:lastInDateEnd))") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!lastOutDateStart.isNullOrBlank()) { |
|
|
if (!lastOutDateStart.isNullOrBlank()) { |
|
|
val formattedDate = lastOutDateStart.replace("/", "-") |
|
|
val formattedDate = lastOutDateStart.replace("/", "-") |
|
|
args["lastOutDateStart"] = formattedDate |
|
|
args["lastOutDateStart"] = formattedDate |
|
|
havingConditions.add("(cum_out.lastOutDate IS NOT NULL AND DATE(cum_out.lastOutDate) >= DATE(:lastOutDateStart))") |
|
|
|
|
|
|
|
|
havingConditions.add("(lot_agg.lotLastOutDate IS NOT NULL AND DATE(lot_agg.lotLastOutDate) >= DATE(:lastOutDateStart))") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!lastOutDateEnd.isNullOrBlank()) { |
|
|
if (!lastOutDateEnd.isNullOrBlank()) { |
|
|
val formattedDate = lastOutDateEnd.replace("/", "-") |
|
|
val formattedDate = lastOutDateEnd.replace("/", "-") |
|
|
args["lastOutDateEnd"] = formattedDate |
|
|
args["lastOutDateEnd"] = formattedDate |
|
|
havingConditions.add("(cum_out.lastOutDate IS NOT NULL AND DATE(cum_out.lastOutDate) <= DATE(:lastOutDateEnd))") |
|
|
|
|
|
|
|
|
havingConditions.add("(lot_agg.lotLastOutDate IS NOT NULL AND DATE(lot_agg.lotLastOutDate) <= DATE(:lastOutDateEnd))") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
val finalSql = if (havingConditions.isNotEmpty()) { |
|
|
val finalSql = if (havingConditions.isNotEmpty()) { |
|
|
|