diff --git a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/PSService.kt b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/PSService.kt index 120ab5a9..c65b63e6 100644 --- a/src/main/java/com/ffii/fpsms/modules/jobOrder/service/PSService.kt +++ b/src/main/java/com/ffii/fpsms/modules/jobOrder/service/PSService.kt @@ -29,35 +29,24 @@ open class PSService( "fromDate" to from.toString(), "toDate" to to.toString(), ) + // Join once instead of per-FG correlated scans on delivery_order_line (~millions of rows on demo copy). val sql = """ SELECT - (SELECT dailyQty FROM item_daily_out WHERE itemCode = items.code) AS dailyQty, - (SELECT - IFNULL(ROUND(AVG(d.dailyQty)), 0) - FROM - (SELECT - SUM(dol.qty) AS dailyQty - FROM - delivery_order_line dol - LEFT JOIN delivery_order do ON dol.deliveryOrderId = do.id - WHERE - do.deleted = 0 - AND dol.itemId = items.id - AND do.estimatedArrivalDate >= :fromDate AND do.estimatedArrivalDate <= :toDate - GROUP BY do.estimatedArrivalDate) AS d) AS avgQtyLastMonth, - (SELECT SUM(reqQty) FROM job_order WHERE bomId = bom.id AND status != 'completed') AS pendingJobQty, - (SELECT COUNT(1) FROM coffee_or_tea WHERE systemType = 'coffee' AND itemCode = items.code AND deleted = 0) AS isCoffee, - (SELECT COUNT(1) FROM coffee_or_tea WHERE systemType = 'tea' AND itemCode = items.code AND deleted = 0) AS isTea, - (SELECT COUNT(1) FROM coffee_or_tea WHERE systemType = 'lemon' AND itemCode = items.code AND deleted = 0) AS isLemon, + ido.dailyQty AS dailyQty, + COALESCE(avg_out.avgQtyLastMonth, 0) AS avgQtyLastMonth, + pending.pendingJobQty AS pendingJobQty, + COALESCE(cot.isCoffee, 0) AS isCoffee, + COALESCE(cot.isTea, 0) AS isTea, + COALESCE(cot.isLemon, 0) AS isLemon, CASE WHEN item_fake_onhand.onHandQty IS NOT NULL THEN item_fake_onhand.onHandQty ELSE inventory.onHandQty - 500 END AS stockQty, bom.baseScore, bom.outputQty, bom.outputQtyUom, - (SELECT udfudesc - FROM delivery_order_line - LEFT JOIN uom_conversion ON delivery_order_line.uomId = uom_conversion.id - WHERE delivery_order_line.itemId = bom.itemId + (SELECT uc.udfudesc + FROM delivery_order_line dol + LEFT JOIN uom_conversion uc ON dol.uomId = uc.id + WHERE dol.itemId = bom.itemId LIMIT 1) AS doUom, items.code AS itemCode, items.name AS itemName, @@ -81,6 +70,35 @@ open class PSService( LEFT JOIN item_fake_onhand ON items.code = item_fake_onhand.itemCode LEFT JOIN item_uom iu ON iu.itemId = items.id AND iu.stockUnit = 1 LEFT JOIN uom_conversion uc_stock ON uc_stock.id = iu.uomId + LEFT JOIN item_daily_out ido ON ido.itemCode = items.code + LEFT JOIN ( + SELECT itemId, IFNULL(ROUND(AVG(dailyQty)), 0) AS avgQtyLastMonth + FROM ( + SELECT dol.itemId, do.estimatedArrivalDate, SUM(dol.qty) AS dailyQty + FROM delivery_order_line dol + INNER JOIN delivery_order do ON dol.deliveryOrderId = do.id + WHERE do.deleted = 0 + AND do.estimatedArrivalDate >= :fromDate + AND do.estimatedArrivalDate <= :toDate + GROUP BY dol.itemId, do.estimatedArrivalDate + ) d + GROUP BY itemId + ) avg_out ON avg_out.itemId = items.id + LEFT JOIN ( + SELECT bomId, SUM(reqQty) AS pendingJobQty + FROM job_order + WHERE status != 'completed' + GROUP BY bomId + ) pending ON pending.bomId = bom.id + LEFT JOIN ( + SELECT itemCode, + SUM(CASE WHEN systemType = 'coffee' THEN 1 ELSE 0 END) AS isCoffee, + SUM(CASE WHEN systemType = 'tea' THEN 1 ELSE 0 END) AS isTea, + SUM(CASE WHEN systemType = 'lemon' THEN 1 ELSE 0 END) AS isLemon + FROM coffee_or_tea + WHERE deleted = 0 + GROUP BY itemCode + ) cot ON cot.itemCode = items.code WHERE bom.deleted = 0 and bom.description = 'FG' """.trimIndent() return jdbcDao.queryForList(sql, args) diff --git a/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt b/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt index 1add6b92..bbb9cc92 100644 --- a/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt +++ b/src/main/java/com/ffii/fpsms/modules/master/service/ProductionScheduleService.kt @@ -1656,7 +1656,8 @@ open class ProductionScheduleService( val dateFormatterDayMonth = DateTimeFormatter.ofPattern("d MMM", Locale.ENGLISH) val dateFormatterDayName = DateTimeFormatter.ofPattern("EEEE", Locale.ENGLISH) - val dateInfo = (1..9).map { offset -> + // Match searchExportDailyMaterial day1..day7 columns + val dateInfo = (1..7).map { offset -> val date = fromDate.plusDays(offset.toLong()) Pair(date.format(dateFormatterDayMonth), date.format(dateFormatterDayName)) } @@ -1693,7 +1694,7 @@ open class ProductionScheduleService( rowData["uom"]?.toString() ?: "" ) - val dailyQty = (1..9).map { offset -> + val dailyQty = (1..7).map { offset -> asDouble(rowData["day$offset"]) } @@ -1722,7 +1723,7 @@ open class ProductionScheduleService( dailySheet.setColumnWidth(0, 20 * 256) // 物料編號 dailySheet.setColumnWidth(1, 55 * 256) // 物料名稱 dailySheet.setColumnWidth(2, 12 * 256) // 採購單位 - for (i in baseHeaders.size until baseHeaders.size + 9) { + for (i in baseHeaders.size until baseHeaders.size + 7) { dailySheet.setColumnWidth(i, 16 * 256) // daily qty columns } @@ -1792,31 +1793,32 @@ open class ProductionScheduleService( "fromDate" to fromDate, ) + // Aggregate-only SELECT: MySQL 8 ONLY_FULL_GROUP_BY rejects the old `group by matCode` + psl.*. val sql = """ select group_concat(distinct it.code) as itemCode, group_concat(distinct it.name) as itemName, itm.code as matCode, - itm.name as matName, + MAX(itm.name) as matName, sum(bm.qty * psl.batchNeed) as totalMatQtyNeed, - iv.onHandQty, - iv.unavailableQty, + MAX(iv.onHandQty) as onHandQty, + MAX(iv.unavailableQty) as unavailableQty, (select sum(qty) from purchase_order_line left join purchase_order on purchase_order_line.purchaseOrderId = purchase_order.id where purchase_order_line.itemId = itm.id and date(purchase_order.estimatedArrivalDate) >= date(now()) and purchase_order.completeDate is null) as purchasedQty, - bm.uomName, - (select ratioD/ratioN from item_uom where purchaseUnit = 1 and itemId = it.id and item_uom.deleted = 0 - ) as purchaseRatio, - (select ceil(ratioD/ratioN*sum(bm.qty * psl.batchNeed)) from item_uom where purchaseUnit = 1 and itemId = it.id and item_uom.deleted = 0 - ) as purchaseQtyNeed, + MAX(bm.uomName) as uomName, + (select ratioD/ratioN from item_uom where purchaseUnit = 1 and itemId = itm.id and item_uom.deleted = 0 + LIMIT 1) as purchaseRatio, + ceil(sum(bm.qty * psl.batchNeed) * COALESCE(( + select ratioD/ratioN from item_uom where purchaseUnit = 1 and itemId = itm.id and item_uom.deleted = 0 LIMIT 1 + ), 0)) as purchaseQtyNeed, (select code from item_uom left join uom_conversion on item_uom.uomId = uom_conversion.id - where item_uom.purchaseUnit = 1 and item_uom.itemId = it.id and item_uom.deleted = 0 - ) as purchaseUnit, + where item_uom.purchaseUnit = 1 and item_uom.itemId = itm.id and item_uom.deleted = 0 + LIMIT 1) as purchaseUnit, min(ps.produceAt) as toProdcueFrom, max(ps.produceAt) as toProdcueAt, - ucs.udfudesc as stockUnit, - psl.* + MAX(ucs.udfudesc) as stockUnit from production_schedule_line psl left join production_schedule ps on psl.prodScheduleId = ps.id @@ -1825,15 +1827,15 @@ open class ProductionScheduleService( left join bom_material bm on bom.id = bm.bomId left join items itm on bm.itemId = itm.id left join inventory iv on itm.id = iv.itemId - left join item_uom ius on itm.id = ius.itemId and ius.stockUnit = 1 + left join item_uom ius on itm.id = ius.itemId and ius.stockUnit = 1 and ius.deleted = 0 left join uom_conversion ucs on ius.uomId = ucs.id where ps.produceAt >= :fromDate and ps.id = (select id from production_schedule where produceAt = ps.produceAt order by id desc limit 1) -- This is water AND it.code != 'GI3236' - -- order by ps.produceAt asc, psl.itemPriority desc, itemCode asc - group by matCode + AND itm.id IS NOT NULL + group by itm.id, itm.code """; @@ -1869,33 +1871,33 @@ open class ProductionScheduleService( daily_needs AS ( SELECT itm.id AS materialId, - itm.code AS matCode, - itm.name AS matName, - COALESCE(uomM18.code, uomP.code) AS uom, - ceil((iv.onHandQty * (itsm.ratioN / itsm.ratioD)) * (COALESCE(ium18.ratioD, itum.ratioD) / COALESCE(ium18.ratioN, itum.ratioN))) as onHandQty, - ceil((iv.unavailableQty * (itsm.ratioN / itsm.ratioD)) * (COALESCE(ium18.ratioD, itum.ratioD) / COALESCE(ium18.ratioN, itum.ratioN))) as unavailableQty, - COALESCE(( + MAX(itm.code) AS matCode, + MAX(itm.name) AS matName, + MAX(COALESCE(uomM18.code, uomP.code)) AS uom, + MAX(ceil((iv.onHandQty * (itsm.ratioN / itsm.ratioD)) * (COALESCE(ium18.ratioD, itum.ratioD) / COALESCE(ium18.ratioN, itum.ratioN)))) as onHandQty, + MAX(ceil((iv.unavailableQty * (itsm.ratioN / itsm.ratioD)) * (COALESCE(ium18.ratioD, itum.ratioD) / COALESCE(ium18.ratioN, itum.ratioN)))) as unavailableQty, + MAX(COALESCE(( SELECT ceil(SUM(pol.qty * (itum.ratioN / itum.ratioD) * (COALESCE(ium18.ratioD, itum.ratioD) / COALESCE(ium18.ratioN, itum.ratioN)))) FROM purchase_order_line pol JOIN purchase_order po ON pol.purchaseOrderId = po.id WHERE pol.itemId = itm.id AND DATE(po.estimatedArrivalDate) >= :fromDate AND po.completeDate IS NULL - ), 0) AS purchasedQty, + ), 0)) AS purchasedQty, DATE(ps.produceAt) AS produceDate, - ceil( SUM(bm.baseQty * psl.batchNeed) * (COALESCE(ium18.ratioD, itum.ratioD) / COALESCE(ium18.ratioN, itum.ratioN)) ) AS qtyNeeded + ceil( SUM(bm.baseQty * psl.batchNeed) * MAX(COALESCE(ium18.ratioD, itum.ratioD) / COALESCE(ium18.ratioN, itum.ratioN)) ) AS qtyNeeded FROM production_schedule_line psl JOIN production_schedule ps ON psl.prodScheduleId = ps.id JOIN items it ON psl.itemId = it.id JOIN bom ON it.id = bom.itemId JOIN bom_material bm ON bom.id = bm.bomId JOIN items itm ON bm.itemId = itm.id - JOIN item_uom itum ON itm.id = itum.itemId and itum.purchaseUnit = 1 + JOIN item_uom itum ON itm.id = itum.itemId and itum.purchaseUnit = 1 and itum.deleted = 0 JOIN uom_conversion uomP ON itum.uomId = uomP.id LEFT JOIN latest_supplier_uom lsu ON lsu.materialId = itm.id LEFT JOIN item_uom ium18 ON ium18.itemId = itm.id AND ium18.uomId = lsu.uomIdM18 AND ium18.deleted = 0 LEFT JOIN uom_conversion uomM18 ON uomM18.id = ium18.uomId - JOIN item_uom itsm ON itm.id = itsm.itemId and itsm.stockUnit = 1 + JOIN item_uom itsm ON itm.id = itsm.itemId and itsm.stockUnit = 1 and itsm.deleted = 0 LEFT JOIN inventory iv ON itm.id = iv.itemId WHERE DATE(ps.produceAt) >= DATE_ADD(:fromDate, INTERVAL 1 DAY) AND DATE(ps.produceAt) < DATE_ADD(:fromDate, INTERVAL 8 DAY)