Files
BE_IBL/application/controllers/tools/N8n_company.php
2026-04-15 15:23:57 +07:00

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;
}
}