LEFT JOIN stock_in_line sil ON sl.stockInLineId = sil.id AND sil.deleted = 0
LEFT JOIN purchase_order_line pol_in ON sil.purchaseOrderLineId = pol_in.id AND pol_in.deleted = 0
LEFT JOIN item_uom iu_stock ON sl.itemId = iu_stock.itemId AND iu_stock.stockUnit = 1 AND iu_stock.deleted = 0
LEFT JOIN item_uom iu_purchase ON sl.itemId = iu_purchase.itemId AND iu_purchase.purchaseUnit = 1 AND iu_purchase.deleted = 0
INNER JOIN round_time rt ON 1 = 1
INNER JOIN prev_round_time prt ON 1 = 1
WHERE sl.deleted = 0
AND sl.itemCode IS NOT NULL AND sl.itemCode <> ''
AND sl.created >= prt.prevRoundStart
AND sl.created < rt.roundStart
AND COALESCE(sl.inQty, 0) > 0
AND pol_in.id IS NOT NULL
AND iu_stock.id IS NOT NULL
AND iu_purchase.id IS NOT NULL
AND COALESCE(iu_purchase.ratioN, 0) > 0
AND UPPER(TRIM(COALESCE(sl.type, ''))) NOT IN ('TKE', 'OPEN', 'ADJ')
),
prev_window_cost_per_item AS (
SELECT
ranked.itemId,
ranked.stockUnitCost
FROM (
SELECT
b.itemId,
b.stockUnitCost,
ROW_NUMBER() OVER (
PARTITION BY b.itemId
ORDER BY b.ledgerCreated DESC, b.slId DESC
) AS rn
FROM prev_window_cost_base b
WHERE COALESCE(b.stockUnitCost, 0) > 0
) ranked
WHERE ranked.rn = 1
),
ledger_flagged AS (
SELECT
lw.*,
COALESCE(lt.tkeBalance, 0) AS openingBalancePerItem,
COALESCE(pwc.stockUnitCost, 0) AS prevWindowStockUnitCost,
CASE
WHEN lt.tkeCreated IS NULL THEN 1
WHEN lw.ledgerCreated > lt.tkeCreated THEN 1
WHEN lw.ledgerCreated = lt.tkeCreated AND lw.slId > lt.tkeId THEN 1
ELSE 0
END AS isMovementRow
FROM ledger_window lw
LEFT JOIN latest_tke_in_window_per_item lt ON lt.itemId = lw.itemId
LEFT JOIN prev_window_cost_per_item pwc ON pwc.itemId = lw.itemId
)
SELECT
SELECT
'' as stockSubCategory,
'' as stockSubCategory,
COALESCE(item_agg.itemNo, '') as itemNo,
COALESCE(item_agg.itemNo, '') as itemNo,
@@ -1174,80 +1307,103 @@ return result
SUM(agg.total_out_value) AS total_out_value
SUM(agg.total_out_value) AS total_out_value
FROM (
FROM (
SELECT
SELECT
sl.itemCode,
sl.itemId,
COALESCE(il_in.id, il_out.id) AS lotId,
lf.itemCode,
lf.itemId,
MAX(lf.openingBalancePerItem) AS openingBalance,
SUM(
SUM(
CASE
CASE
WHEN DATE(sl.date) <= :fromDate
THEN COALESCE(sl.inQty, 0) - COALESCE(sl.outQty, 0)
WHEN lf.isMovementRow = 1
AND lf.normType NOT IN ('OPEN', 'TKE')
THEN COALESCE(lf.inQty, 0)
ELSE 0
ELSE 0
END
END
) AS openingBalance,
) AS cumStockIn,
SUM(
SUM(
CASE
CASE
WHEN DATE(sl.date) > :fromDate
AND DATE(sl.date) <= :toDate
AND UPPER(TRIM(COALESCE(sl.type, ''))) <> 'TKE'
AND sil.stockTakeLineId IS NULL
THEN COALESCE(sl.inQty, 0)
WHEN COALESCE(lf.outQty, 0) > 0
AND lf.isMovementRow = 1
AND lf.normType NOT IN (
'OPEN', 'MISS', 'BAD', 'TKE', 'ADJ', 'STOCKTAKE'
)
THEN COALESCE(lf.outQty, 0)
ELSE 0
ELSE 0
END
END
) AS cumStockIn,
) AS cumStockOut,
MAX(lf.openingBalancePerItem) + SUM(
CASE
WHEN lf.isMovementRow = 1
THEN COALESCE(lf.inQty, 0) - COALESCE(lf.outQty, 0)
ELSE 0
END
) AS currentBalance,
MAX(CASE WHEN lf.isMovementRow = 1 AND COALESCE(lf.inQty, 0) > 0 THEN lf.ledgerDate END) AS lastInDate,
MAX(CASE WHEN lf.isMovementRow = 1 AND COALESCE(lf.outQty, 0) > 0 THEN lf.ledgerDate END) AS lastOutDate,
MAX(lf.storeLocation) AS storeLocation,
SUM(
SUM(
CASE
CASE
WHEN DATE(sl.date) > :fromDate
AND DATE(sl.date) <= :toDate
AND COALESCE(sl.outQty, 0) > 0
AND UPPER(TRIM(COALESCE(sl.type, ''))) <> 'TKE'
AND NOT (
LOWER(TRIM(COALESCE(sl.type, ''))) = 'stocktake'
OR (
LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj'
AND (sol.stockTransferId IS NULL OR sol.id IS NULL)
)
OR LOWER(TRIM(COALESCE(sl.type, ''))) = 'miss'
OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'miss'
OR LOWER(TRIM(COALESCE(sl.type, ''))) = 'bad'
OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'bad'
)
THEN COALESCE(sl.outQty, 0)
WHEN lf.isMovementRow = 1
AND COALESCE(lf.outQty, 0) > 0
AND lf.normType = 'MISS'
THEN COALESCE(lf.outQty, 0)
ELSE 0
ELSE 0
END
END
) AS cumStockOut,
SUM(CASE WHEN DATE(sl.date) <= :toDate THEN COALESCE(sl.inQty, 0) - COALESCE(sl.outQty, 0) ELSE 0 END) AS currentBalance,
MAX(CASE WHEN COALESCE(sl.inQty, 0) > 0 THEN sl.date END) AS lastInDate,
MAX(CASE WHEN COALESCE(sl.outQty, 0) > 0 THEN sl.date END) AS lastOutDate,
MAX(lot_wh.storeLocation) AS storeLocation,
SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.inQty, 0) > 0 AND (LOWER(TRIM(COALESCE(sl.type, ''))) = 'miss' OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'miss') THEN sl.outQty ELSE 0 END) AS cumStockOutMiss,
SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND (LOWER(TRIM(COALESCE(sl.type, ''))) = 'bad' OR LOWER(TRIM(COALESCE(sol.type, ''))) = 'bad') THEN sl.outQty ELSE 0 END) AS cumStockOutBad,
SUM(CASE WHEN DATE(sl.date) BETWEEN :fromDate AND :toDate AND COALESCE(sl.outQty, 0) > 0 AND LOWER(TRIM(COALESCE(sl.type, ''))) = 'adj' AND (sol.stockTransferId IS NULL OR sol.id IS NULL) THEN sl.outQty ELSE 0 END) AS cumStockOutAdjStockTake,
SUM(COALESCE(sl.inQty, 0) * CASE WHEN pol_in.id IS NOT NULL AND iu_stock.id IS NOT NULL AND iu_purchase.id IS NOT NULL AND COALESCE(iu_purchase.ratioN, 0) > 0 THEN COALESCE(pol_in.up, 0) * (iu_stock.ratioN / NULLIF(iu_stock.ratioD, 0)) / (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) ELSE 0 END) AS total_in_value,
SUM(COALESCE(sl.outQty, 0) * CASE WHEN pol_out.id IS NOT NULL AND iu_stock.id IS NOT NULL AND iu_purchase.id IS NOT NULL AND COALESCE(iu_purchase.ratioN, 0) > 0 THEN COALESCE(pol_out.up, 0) * (iu_stock.ratioN / NULLIF(iu_stock.ratioD, 0)) / (iu_purchase.ratioN / NULLIF(iu_purchase.ratioD, 0)) ELSE 0 END) AS total_out_value
FROM stock_ledger sl
LEFT JOIN stock_in_line sil ON sl.stockInLineId = sil.id AND sil.deleted = 0
LEFT JOIN inventory_lot il_in ON sil.inventoryLotId = il_in.id AND il_in.deleted = 0
LEFT JOIN stock_out_line sol ON sl.stockOutLineId = sol.id AND sol.deleted = 0
LEFT JOIN inventory_lot_line ill_out ON sol.inventoryLotLineId = ill_out.id AND ill_out.deleted = 0
LEFT JOIN inventory_lot il_out ON ill_out.inventoryLotId = il_out.id AND il_out.deleted = 0
LEFT JOIN (
SELECT il.id AS lotId, MAX(wh.code) AS storeLocation
FROM inventory_lot il
LEFT JOIN inventory_lot_line ill ON ill.inventoryLotId = il.id AND ill.deleted = 0
LEFT JOIN warehouse wh ON ill.warehouseId = wh.id AND wh.deleted = 0
GROUP BY il.id
) lot_wh ON lot_wh.lotId = COALESCE(il_in.id, il_out.id)
LEFT JOIN purchase_order_line pol_in ON sil.purchaseOrderLineId = pol_in.id AND pol_in.deleted = 0
LEFT JOIN stock_in_line sil_out ON il_out.stockInLineId = sil_out.id AND sil_out.deleted = 0
LEFT JOIN purchase_order_line pol_out ON sil_out.purchaseOrderLineId = pol_out.id AND pol_out.deleted = 0
LEFT JOIN item_uom iu_stock ON sl.itemId = iu_stock.itemId AND iu_stock.stockUnit = 1 AND iu_stock.deleted = 0
LEFT JOIN item_uom iu_purchase ON sl.itemId = iu_purchase.itemId AND iu_purchase.purchaseUnit = 1 AND iu_purchase.deleted = 0
WHERE sl.deleted = 0
AND sl.itemCode IS NOT NULL AND sl.itemCode <> ''
AND DATE(sl.date) <= :toDate
AND COALESCE(il_in.id, il_out.id) IS NOT NULL
$itemCodeSql
GROUP BY sl.itemCode, sl.itemId, COALESCE(il_in.id, il_out.id)
) AS cumStockOutMiss,
SUM(
CASE
WHEN lf.isMovementRow = 1
AND COALESCE(lf.outQty, 0) > 0
AND lf.normType = 'BAD'
THEN COALESCE(lf.outQty, 0)
ELSE 0
END
) AS cumStockOutBad,
SUM(
CASE
WHEN lf.isMovementRow = 1
AND COALESCE(lf.outQty, 0) > 0
AND lf.normType IN ('TKE', 'ADJ', 'STOCKTAKE')
THEN COALESCE(lf.outQty, 0)
ELSE 0
END
) AS cumStockOutAdjStockTake,
SUM(
CASE
WHEN lf.isMovementRow = 1
THEN
COALESCE(lf.inQty, 0) * CASE
WHEN lf.normType = 'TKE' THEN COALESCE(lf.prevWindowStockUnitCost, 0)
CASE WHEN COALESCE(bp.qtySum, 0) > 0 THEN (bp.amtSum / bp.qtySum) ELSE 0 END
ELSE
CASE WHEN COALESCE(np.qtySum, 0) > 0 THEN (np.amtSum / np.qtySum) ELSE 0 END
END AS avgUnitPriceRaw
FROM item_scope s
LEFT JOIN is_bom_item b ON b.itemId = s.itemId
LEFT JOIN bom_price bp ON bp.itemId = s.itemId
LEFT JOIN non_bom_price np ON np.itemId = s.itemId
)
SELECT
'' AS stockSubCategory,
COALESCE(s.itemNo, '') AS itemNo,
COALESCE(s.itemName, '') AS itemName,
COALESCE(uc.udfudesc, uc.code, '') AS unitOfMeasure,
'' AS lotNo,
'' AS expiryDate,
'' AS openingBalance,
'' AS cumStockIn,
'' AS cumStockOut,
'' AS currentBalance,
'' AS reOrderQty,
COALESCE(store_location.storeLocation, '') AS storeLocation,
COALESCE(DATE_FORMAT(lio.lastInDate, '%Y-%m-%d'), '') AS lastInDate,
COALESCE(DATE_FORMAT(lio.lastOutDate, '%Y-%m-%d'), '') AS lastOutDate,
COALESCE(op.openingBalance, 0) AS openingBalanceRaw,
COALESCE(cp.currentBalance, 0) AS currentBalanceRaw,
CASE WHEN COALESCE(op.openingBalance, 0) < 0 THEN CONCAT('(', FORMAT(-op.openingBalance, 0), ')') ELSE FORMAT(COALESCE(op.openingBalance, 0), 0) END AS totalOpeningBalance,
'0' AS totalCumStockIn,
'0' AS totalCumStockOut,
CASE WHEN COALESCE(cp.currentBalance, 0) < 0 THEN CONCAT('(', FORMAT(-cp.currentBalance, 0), ')') ELSE FORMAT(COALESCE(cp.currentBalance, 0), 0) END AS totalCurrentBalance,
'' AS misInputAndLost,
'' AS defectiveGoods,
'' AS variance,
'0' AS totalMisInputAndLost,
'0' AS totalVariance,
'0' AS totalDefectiveGoods,
FORMAT(ROUND(COALESCE(ap.avgUnitPriceRaw, 0), 2), 2) AS avgUnitPrice,
FORMAT(ROUND(COALESCE(ap.avgUnitPriceRaw, 0) * COALESCE(cp.currentBalance, 0), 2), 2) AS totalStockBalance
FROM item_scope s
LEFT JOIN opening_per_item op ON op.itemId = s.itemId
LEFT JOIN current_per_item cp ON cp.itemId = s.itemId
LEFT JOIN last_in_out lio ON lio.itemId = s.itemId
LEFT JOIN avg_price_per_item ap ON ap.itemId = s.itemId
LEFT JOIN item_uom iu ON iu.itemId = s.itemId AND iu.stockUnit = 1 AND iu.deleted = 0
LEFT JOIN uom_conversion uc ON iu.uomId = uc.id
LEFT JOIN store_location ON store_location.itemId = s.itemId