false, 'error' => 'Invalid date format. Use YYYYMMDD (e.g. 20260521)']); } // ============================================= // 메인 쿼리 // tbl_daily → 수거 데이터 (Forecast, Actual, Stops) // tbl_member → 드라이버 조인 조건 // tbl_memberhis → 출퇴근/Cash 데이터 (LEFT JOIN: 없어도 수거 데이터는 나옴) // ============================================= $sql = " SELECT d.d_orderdate AS order_date, d.d_druid AS driver_uid, d.d_drinitial AS driver, -- 수거 데이터 COUNT(*) AS total_stops, COUNT(CASE WHEN d.d_status = 'F' THEN 1 END) AS completed_stops, COALESCE(SUM(d.d_estquantity), 0) AS forecast, COALESCE(SUM(d.d_quantity), 0) AS actual, -- 출퇴근 시간 (tbl_memberhis) h.h_departuretime AS departure, h.h_arrivaltime AS arrival, -- 마일리지 (tbl_memberhis) h.h_mileage_s AS mileage_start, h.h_mileage_f AS mileage_finish, -- Cash 데이터 (tbl_memberhis) COALESCE(h.h_balance_o, 0) AS opening_balance, COALESCE(h.h_balance_in, 0) AS cash_in, COALESCE(h.h_balance_out, 0) AS cash_out, COALESCE(h.h_balance_r, 0) AS cash_return, COALESCE(h.h_balance_g, 0) AS gas, COALESCE(h.h_balance_e, 0) AS extra, COALESCE(h.h_balance_t, 0) AS balance_total, -- 드라이버 코멘트 h.h_comment AS comment FROM tbl_daily d INNER JOIN tbl_member m ON (d.d_druid = m.m_currentdriver) LEFT JOIN tbl_memberhis h ON (d.d_druid = h.h_druid AND d.d_orderdate = h.h_date) WHERE d.d_orderdate BETWEEN :date_from AND :date_to "; if ($driver) { $sql .= " AND d.d_drinitial = :driver "; } $sql .= " GROUP BY d.d_orderdate, d.d_drinitial, d.d_druid, h.h_departuretime, h.h_arrivaltime, h.h_mileage_s, h.h_mileage_f, h.h_balance_o, h.h_balance_in, h.h_balance_out, h.h_balance_r, h.h_balance_g, h.h_balance_e, h.h_balance_t, h.h_comment ORDER BY d.d_orderdate DESC, d.d_drinitial ASC "; $stmt = $pdo->prepare($sql); $stmt->bindParam(':date_from', $dateFrom); $stmt->bindParam(':date_to', $dateTo); if ($driver) { $stmt->bindParam(':driver', $driver); } $stmt->execute(); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); // ============================================= // 계산 컬럼 추가 // ============================================= foreach ($rows as &$row) { // 날짜 포맷 변환 YYYYMMDD → YYYY-MM-DD $d = $row['order_date']; if (strlen($d) === 8) { $row['order_date_formatted'] = substr($d,0,4).'-'.substr($d,4,2).'-'.substr($d,6,2); } // 달성률 (%) $fc = floatval($row['forecast']); $ac = floatval($row['actual']); $row['achievement_pct'] = $fc > 0 ? round($ac / $fc * 100, 1) : 0; // 잔여 수거량 $row['remaining'] = max($fc - $ac, 0); // 진행 상태 if ($ac <= 0) $row['status'] = 'Pending'; elseif ($ac >= $fc) $row['status'] = 'Done'; else $row['status'] = 'In Progress'; // 마일리지 주행거리 $ms = intval($row['mileage_start']); $mf = intval($row['mileage_finish']); $row['mileage_driven'] = ($ms > 0 && $mf > 0) ? max($mf - $ms, 0) : null; } unset($row); jsonResponse([ 'success' => true, 'date_from' => $dateFrom, 'date_to' => $dateTo, 'count' => count($rows), 'data' => $rows ]);