269 lines
10 KiB
PHP
269 lines
10 KiB
PHP
<?php
|
|
defined('BASEPATH') or exit('No direct script access allowed');
|
|
|
|
class N8n_company extends My_Controller
|
|
{
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
public function 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) : 30;
|
|
$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,150
|
|
";
|
|
|
|
$query = $this->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;
|
|
}
|
|
}
|