db->query($sql, $params); $data = $query->result_array(); $max = [ 'recency' => 365, 'frequency' => 100, 'monetary' => 1000000, 'payment_delay' => 90, 'item_breadth' => 50, ]; $today = new DateTime(); foreach ($data as &$row) { $recency_days = 999; if (!empty($row['last_order_date'])) { $recency_days = $today->diff(new DateTime($row['last_order_date']))->days; } $order_count = (int) $row['order_count']; $total_sales = (float) $row['total_sales']; $avg_payment_delay = isset($row['avg_payment_delay']) ? (float)$row['avg_payment_delay'] : $max['payment_delay']; $distinct_tests = (int) $row['distinct_tests']; $recency_score = max(0, 100 - min(($recency_days / $max['recency']) * 100, 100)); $frequency_score = min(($order_count / $max['frequency']) * 100, 100); $monetary_score = min(($total_sales / $max['monetary']) * 100, 100); $payment_score = max(0, 100 - min(($avg_payment_delay / $max['payment_delay']) * 100, 100)); $breadth_score = min(($distinct_tests / $max['item_breadth']) * 100, 100); $final_score = ( 0.25 * $recency_score + 0.25 * $frequency_score + 0.25 * $monetary_score + 0.15 * $payment_score + 0.10 * $breadth_score ); $row['recency_days'] = $recency_days; $row['score_recency'] = round($recency_score, 2); $row['score_frequency'] = round($frequency_score, 2); $row['score_monetary'] = round($monetary_score, 2); $row['score_payment'] = round($payment_score, 2); $row['score_breadth'] = round($breadth_score, 2); $row['score_final'] = round($final_score, 2); } usort($data, function ($a, $b) { return $b['score_final'] <=> $a['score_final']; }); $top_data = array_slice($data, 0, $top); // Only for top data, compute monthly_sales & avg_days_between_orders foreach ($top_data as &$row) { $pattern = $this->analyze_order_pattern($row['M_CompanyID'], $start_date, $end_date); $row['avg_days_between_orders'] = $pattern['avg_days_between_orders']; $row['monthly_sales'] = $pattern['monthly_sales']; } header('Content-Type: application/json'); echo json_encode([ 'status' => 'success', 'start_date' => $start_date, 'end_date' => $end_date, 'top' => $top, 'total_companies' => count($data), 'data' => $top_data ]); exit; } public function old_predictive($start_date = '', $end_date = '', $top = 30) { if (!$start_date || !$end_date) { $end_date = date('Y-m-d'); $start_date = date('Y-m-d', strtotime('-6 months')); } $top = is_numeric($top) ? intval($top) : 50; $params = [$start_date, $end_date]; $sql = " SELECT c.M_CompanyID, c.M_CompanyName, MAX(o.T_OrderHeaderDate) AS last_order_date, COUNT(DISTINCT o.T_OrderHeaderID) AS order_count, SUM(o.T_OrderHeaderTotal) AS total_sales, MAX(p.F_PaymentDate) AS last_payment_date, SUM(p.F_PaymentTotal) AS total_payment, AVG(DATEDIFF(p.F_PaymentDate, o.T_OrderHeaderDate)) AS avg_payment_delay, COUNT(DISTINCT od.T_OrderDetailT_TestName) AS distinct_tests FROM m_company c JOIN t_orderheader o ON o.T_OrderHeaderM_CompanyID = c.M_CompanyID AND o.T_OrderHeaderIsActive = 'Y' AND o.T_OrderHeaderDate BETWEEN ? AND ? JOIN f_payment p ON p.F_PaymentT_OrderHeaderID = o.T_OrderHeaderID AND p.F_PaymentIsActive = 'Y' JOIN t_orderdetail od ON od.T_OrderDetailT_OrderHeaderID = o.T_OrderHeaderID AND od.T_OrderDetailIsActive = 'Y' WHERE c.M_CompanyIsActive = 'Y' GROUP BY c.M_CompanyID order by total_sales desc limit 0,200 "; $query = $this->db->query($sql, $params); $data = $query->result_array(); // Scoring max thresholds $max = [ 'recency' => 365, 'frequency' => 100, 'monetary' => 1000000, 'payment_delay' => 90, 'item_breadth' => 50, ]; $today = new DateTime(); foreach ($data as &$row) { $recency_days = 999; if (!empty($row['last_order_date'])) { $recency_days = $today->diff(new DateTime($row['last_order_date']))->days; } $order_count = (int) $row['order_count']; $total_sales = (float) $row['total_sales']; $avg_payment_delay = isset($row['avg_payment_delay']) ? (float)$row['avg_payment_delay'] : $max['payment_delay']; $distinct_tests = (int) $row['distinct_tests']; // Score calculation $recency_score = max(0, 100 - min(($recency_days / $max['recency']) * 100, 100)); $frequency_score = min(($order_count / $max['frequency']) * 100, 100); $monetary_score = min(($total_sales / $max['monetary']) * 100, 100); $payment_score = max(0, 100 - min(($avg_payment_delay / $max['payment_delay']) * 100, 100)); $breadth_score = min(($distinct_tests / $max['item_breadth']) * 100, 100); $final_score = ( 0.25 * $recency_score + 0.25 * $frequency_score + 0.25 * $monetary_score + 0.15 * $payment_score + 0.10 * $breadth_score ); $row['recency_days'] = $recency_days; $row['score_recency'] = round($recency_score, 2); $row['score_frequency'] = round($frequency_score, 2); $row['score_monetary'] = round($monetary_score, 2); $row['score_payment'] = round($payment_score, 2); $row['score_breadth'] = round($breadth_score, 2); $row['score_final'] = round($final_score, 2); } // Sort by score // usort($data, fn($a, $b) => $b['score_final'] <=> $a['score_final']); usort($data, function ($a, $b) { return $b['score_final'] <=> $a['score_final']; }); $top_data = array_slice($data, 0, $top); // Output JSON header('Content-Type: application/json'); echo json_encode([ 'status' => 'success', 'start_date' => $start_date, 'end_date' => $end_date, 'top' => $top, 'total_companies' => count($data), 'data' => $top_data ]); exit; } private function analyze_order_pattern($company_id, $start_date, $end_date) { $result = [ 'monthly_sales' => [], 'avg_days_between_orders' => null ]; $orders = $this->db->query(" SELECT T_OrderHeaderDate, T_OrderHeaderTotal FROM t_orderheader WHERE T_OrderHeaderIsActive = 'Y' AND T_OrderHeaderM_CompanyID = ? AND T_OrderHeaderDate BETWEEN ? AND ? ORDER BY T_OrderHeaderDate ASC ", [$company_id, $start_date, $end_date])->result(); $prev_date = null; $interval_sum = 0; $interval_count = 0; foreach ($orders as $order) { $month = date('Y-m', strtotime($order->T_OrderHeaderDate)); if (!isset($result['monthly_sales'][$month])) { $result['monthly_sales'][$month] = 0; } $result['monthly_sales'][$month] += (float) $order->T_OrderHeaderTotal; if ($prev_date) { $diff_days = (strtotime($order->T_OrderHeaderDate) - strtotime($prev_date)) / (60 * 60 * 24); $interval_sum += $diff_days; $interval_count++; } $prev_date = $order->T_OrderHeaderDate; } if ($interval_count > 0) { $result['avg_days_between_orders'] = round($interval_sum / $interval_count, 1); } return $result; } }