|
|
|
@@ -40,27 +40,28 @@ open class ChartService( |
|
|
|
|
|
|
|
/** |
|
|
|
* Delivery orders: order count and total line qty by date. |
|
|
|
* Uses delivery_order.completeDate or estimatedArrivalDate for date. |
|
|
|
* X-axis date: [delivery_order.estimatedArrivalDate] only (no completeDate/orderDate fallback). |
|
|
|
* Rows without estimatedArrivalDate are excluded. |
|
|
|
*/ |
|
|
|
fun getDeliveryOrderByDate(startDate: LocalDate?, endDate: LocalDate?): List<Map<String, Any>> { |
|
|
|
val args = mutableMapOf<String, Any>() |
|
|
|
val startSql = if (startDate != null) { |
|
|
|
args["startDate"] = startDate.toString() |
|
|
|
"AND DATE(COALESCE(do.completeDate, do.estimatedArrivalDate, do.orderDate)) >= :startDate" |
|
|
|
"AND DATE(do.estimatedArrivalDate) >= :startDate" |
|
|
|
} else "" |
|
|
|
val endSql = if (endDate != null) { |
|
|
|
args["endDate"] = endDate.toString() |
|
|
|
"AND DATE(COALESCE(do.completeDate, do.estimatedArrivalDate, do.orderDate)) <= :endDate" |
|
|
|
"AND DATE(do.estimatedArrivalDate) <= :endDate" |
|
|
|
} else "" |
|
|
|
val sql = """ |
|
|
|
SELECT |
|
|
|
DATE_FORMAT(COALESCE(do.completeDate, do.estimatedArrivalDate, do.orderDate), '%Y-%m-%d') AS date, |
|
|
|
DATE_FORMAT(do.estimatedArrivalDate, '%Y-%m-%d') AS date, |
|
|
|
COUNT(DISTINCT do.id) AS orderCount, |
|
|
|
COALESCE(SUM(dol.qty), 0) AS totalQty |
|
|
|
FROM delivery_order do |
|
|
|
LEFT JOIN delivery_order_line dol ON dol.deliveryOrderId = do.id AND dol.deleted = 0 |
|
|
|
WHERE do.deleted = 0 $startSql $endSql |
|
|
|
GROUP BY DATE(COALESCE(do.completeDate, do.estimatedArrivalDate, do.orderDate)) |
|
|
|
WHERE do.deleted = 0 AND do.estimatedArrivalDate IS NOT NULL $startSql $endSql |
|
|
|
GROUP BY DATE(do.estimatedArrivalDate) |
|
|
|
ORDER BY date |
|
|
|
""".trimIndent() |
|
|
|
return jdbcDao.queryForList(sql, args) |
|
|
|
@@ -529,17 +530,32 @@ open class ChartService( |
|
|
|
* Stock in vs stock out by date. |
|
|
|
* Stock in: stock_in_line.acceptedQty, date from stock_in.completeDate or receiptDate/created. |
|
|
|
* Stock out: stock_out_line.qty, date from stock_out.completeDate or created. |
|
|
|
* |
|
|
|
* Date range is applied inside each UNION branch (predicate pushdown) so we do not aggregate |
|
|
|
* all history before filtering. Reads filtered headers first via STRAIGHT_JOIN (si/so then lines). |
|
|
|
*/ |
|
|
|
fun getStockInOutByDate(startDate: LocalDate?, endDate: LocalDate?): List<Map<String, Any>> { |
|
|
|
val args = mutableMapOf<String, Any>() |
|
|
|
val startSql = if (startDate != null) { |
|
|
|
args["startDate"] = startDate.toString() |
|
|
|
"AND u.dt >= :startDate" |
|
|
|
} else "" |
|
|
|
val endSql = if (endDate != null) { |
|
|
|
args["endDate"] = endDate.toString() |
|
|
|
"AND u.dt <= :endDate" |
|
|
|
} else "" |
|
|
|
if (startDate != null) args["startDate"] = startDate.toString() |
|
|
|
if (endDate != null) args["endDate"] = endDate.toString() |
|
|
|
val inDateFilter = buildString { |
|
|
|
if (startDate != null) { |
|
|
|
append(" AND DATE(COALESCE(si.completeDate, sil.receiptDate, si.created)) >= :startDate") |
|
|
|
} |
|
|
|
if (endDate != null) { |
|
|
|
append(" AND DATE(COALESCE(si.completeDate, sil.receiptDate, si.created)) <= :endDate") |
|
|
|
} |
|
|
|
} |
|
|
|
val outDateFilter = buildString { |
|
|
|
if (startDate != null) { |
|
|
|
append(" AND DATE(COALESCE(so.completeDate, so.created)) >= :startDate") |
|
|
|
} |
|
|
|
if (endDate != null) { |
|
|
|
append(" AND DATE(COALESCE(so.completeDate, so.created)) <= :endDate") |
|
|
|
} |
|
|
|
} |
|
|
|
val startSql = if (startDate != null) "AND u.dt >= :startDate" else "" |
|
|
|
val endSql = if (endDate != null) "AND u.dt <= :endDate" else "" |
|
|
|
val sql = """ |
|
|
|
SELECT DATE_FORMAT(u.dt, '%Y-%m-%d') AS date, |
|
|
|
COALESCE(SUM(u.inQty), 0) AS inQty, |
|
|
|
@@ -547,16 +563,16 @@ open class ChartService( |
|
|
|
FROM ( |
|
|
|
SELECT DATE(COALESCE(si.completeDate, sil.receiptDate, si.created)) AS dt, |
|
|
|
SUM(COALESCE(sil.acceptedQty, 0)) AS inQty, 0 AS outQty |
|
|
|
FROM stock_in_line sil |
|
|
|
INNER JOIN stock_in si ON sil.stockInId = si.id AND si.deleted = 0 |
|
|
|
WHERE sil.deleted = 0 |
|
|
|
FROM stock_in si |
|
|
|
STRAIGHT_JOIN stock_in_line sil ON sil.stockInId = si.id AND sil.deleted = 0 |
|
|
|
WHERE si.deleted = 0$inDateFilter |
|
|
|
GROUP BY DATE(COALESCE(si.completeDate, sil.receiptDate, si.created)) |
|
|
|
UNION ALL |
|
|
|
SELECT DATE(COALESCE(so.completeDate, so.created)) AS dt, |
|
|
|
0 AS inQty, SUM(COALESCE(sol.qty, 0)) AS outQty |
|
|
|
FROM stock_out_line sol |
|
|
|
INNER JOIN stock_out so ON sol.stockOutId = so.id AND so.deleted = 0 |
|
|
|
WHERE sol.deleted = 0 |
|
|
|
FROM stock_out so |
|
|
|
STRAIGHT_JOIN stock_out_line sol ON sol.stockOutId = so.id AND sol.deleted = 0 |
|
|
|
WHERE so.deleted = 0$outDateFilter |
|
|
|
GROUP BY DATE(COALESCE(so.completeDate, so.created)) |
|
|
|
) u |
|
|
|
WHERE 1=1 $startSql $endSql |
|
|
|
@@ -568,23 +584,25 @@ open class ChartService( |
|
|
|
|
|
|
|
/** |
|
|
|
* Distinct items that appear in delivery_order_line in the period (for multi-select options). |
|
|
|
* Period filter: [delivery_order.estimatedArrivalDate] only; null ETA excluded. |
|
|
|
* Uses STRAIGHT_JOIN so MySQL reads filtered `delivery_order` first (avoids full scan on `delivery_order_line`). |
|
|
|
*/ |
|
|
|
fun getTopDeliveryItemsItemOptions(startDate: LocalDate?, endDate: LocalDate?): List<Map<String, Any>> { |
|
|
|
val args = mutableMapOf<String, Any>() |
|
|
|
val startSql = if (startDate != null) { |
|
|
|
args["startDate"] = startDate.toString() |
|
|
|
"AND DATE(COALESCE(do.completeDate, do.estimatedArrivalDate, do.orderDate)) >= :startDate" |
|
|
|
"AND DATE(do.estimatedArrivalDate) >= :startDate" |
|
|
|
} else "" |
|
|
|
val endSql = if (endDate != null) { |
|
|
|
args["endDate"] = endDate.toString() |
|
|
|
"AND DATE(COALESCE(do.completeDate, do.estimatedArrivalDate, do.orderDate)) <= :endDate" |
|
|
|
"AND DATE(do.estimatedArrivalDate) <= :endDate" |
|
|
|
} else "" |
|
|
|
val sql = """ |
|
|
|
SELECT DISTINCT it.code AS itemCode, COALESCE(it.name, '') AS itemName |
|
|
|
FROM delivery_order_line dol |
|
|
|
INNER JOIN delivery_order do ON dol.deliveryOrderId = do.id AND do.deleted = 0 |
|
|
|
INNER JOIN items it ON dol.itemId = it.id AND it.deleted = 0 |
|
|
|
WHERE dol.deleted = 0 $startSql $endSql |
|
|
|
FROM delivery_order do |
|
|
|
STRAIGHT_JOIN delivery_order_line dol ON dol.deliveryOrderId = do.id AND dol.deleted = 0 |
|
|
|
STRAIGHT_JOIN items it ON it.id = dol.itemId AND it.deleted = 0 |
|
|
|
WHERE do.deleted = 0 AND do.estimatedArrivalDate IS NOT NULL $startSql $endSql |
|
|
|
ORDER BY it.code |
|
|
|
""".trimIndent() |
|
|
|
return jdbcDao.queryForList(sql, args) |
|
|
|
@@ -592,6 +610,8 @@ open class ChartService( |
|
|
|
|
|
|
|
/** |
|
|
|
* Top delivery items by total qty in the period. When itemCodes is non-empty, only those items (still ordered by totalQty, limit applied). |
|
|
|
* Period filter: [delivery_order.estimatedArrivalDate] only; null ETA excluded. |
|
|
|
* Uses STRAIGHT_JOIN so MySQL reads filtered `delivery_order` first (avoids full scan on `delivery_order_line`). |
|
|
|
*/ |
|
|
|
fun getTopDeliveryItems( |
|
|
|
startDate: LocalDate?, |
|
|
|
@@ -602,11 +622,11 @@ open class ChartService( |
|
|
|
val args = mutableMapOf<String, Any>("limit" to limit) |
|
|
|
val startSql = if (startDate != null) { |
|
|
|
args["startDate"] = startDate.toString() |
|
|
|
"AND DATE(COALESCE(do.completeDate, do.estimatedArrivalDate, do.orderDate)) >= :startDate" |
|
|
|
"AND DATE(do.estimatedArrivalDate) >= :startDate" |
|
|
|
} else "" |
|
|
|
val endSql = if (endDate != null) { |
|
|
|
args["endDate"] = endDate.toString() |
|
|
|
"AND DATE(COALESCE(do.completeDate, do.estimatedArrivalDate, do.orderDate)) <= :endDate" |
|
|
|
"AND DATE(do.estimatedArrivalDate) <= :endDate" |
|
|
|
} else "" |
|
|
|
val itemSql = if (!itemCodes.isNullOrEmpty()) { |
|
|
|
val codes = itemCodes.map { it.trim() }.filter { it.isNotBlank() } |
|
|
|
@@ -620,10 +640,10 @@ open class ChartService( |
|
|
|
it.code AS itemCode, |
|
|
|
it.name AS itemName, |
|
|
|
SUM(COALESCE(dol.qty, 0)) AS totalQty |
|
|
|
FROM delivery_order_line dol |
|
|
|
INNER JOIN delivery_order do ON dol.deliveryOrderId = do.id AND do.deleted = 0 |
|
|
|
INNER JOIN items it ON dol.itemId = it.id AND it.deleted = 0 |
|
|
|
WHERE dol.deleted = 0 $startSql $endSql $itemSql |
|
|
|
FROM delivery_order do |
|
|
|
STRAIGHT_JOIN delivery_order_line dol ON dol.deliveryOrderId = do.id AND dol.deleted = 0 |
|
|
|
STRAIGHT_JOIN items it ON it.id = dol.itemId AND it.deleted = 0 |
|
|
|
WHERE do.deleted = 0 AND do.estimatedArrivalDate IS NOT NULL $startSql $endSql $itemSql |
|
|
|
GROUP BY dol.itemId, it.code, it.name |
|
|
|
ORDER BY totalQty DESC |
|
|
|
LIMIT :limit |
|
|
|
|