'freshfactory@mim.in', 'password' => '!Werty123' ]); $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => $authUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $authPayload, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json' ] ]); $response = curl_exec($curl); curl_close($curl); if (!$response) { echo "❌ Failed to connect to auth endpoint.\n"; return false; } $json = json_decode($response, true); return $json['token'] ?? false; } // ========== WhatsApp Sender ========== function sendWhatsappAlert($token, $recipients, $bodyValues) { $url = 'https://mimin-api.mimin.io/whatsapp-api/api/v1/whatsapp-dynamic/send-waba-template'; foreach ($recipients as $phone) { $data = [ "name" => "reporting_odoo_v2", "parameter_format" => "POSITIONAL", "phone" => $phone, "category" => "UTILITY", "allow_category_change" => true, "language" => "id", "components" => [ [ "type" => "BODY", "text" => "Tim KKS,\nBerikut laporan kegiatan yg terjadi tgl *{{1}}*_ {{2}}_\n\n*KKS*:\n1. Sales Order # : {{3}}\n2. Sales Order Rp : {{4}}\n3. Delivery Order # : {{5}}\n4. Delivery Order Rp : {{6}}\n5. Sales Invoice # : {{7}}\n6. Sales Invoice Rp : {{8}}\n7. Payment Received # : {{9}}\n8. Payment Received Rp : {{10}}\n9. Return # : {{11}}\n10. Return Rp : {{12}}\n11. DO Cancel # : {{13}}\n12. DO Cancel Rp : {{14}}\n\n*KKD*:\n1. Sales Order # : {{15}}\n2. Sales Order Rp : {{16}}\n3. Delivery Order # : {{17}}\n4. Delivery Order Rp : {{18}}\n5. Sales Invoice # : {{19}}\n6. Sales Invoice Rp : {{20}}\n7. Payment Received # : {{21}}\n8. Payment Received Rp : {{22}}\n9. Return # : {{23}}\n10. Return Rp : {{24}}\n11. DO Cancel # : {{25}}\n12. DO Cancel Rp : {{26}}\n\n*KKR*:\n1. Sales Order # : {{27}}\n2. Sales Order Rp : {{28}}\n3. Delivery Order # : {{29}}\n4. Delivery Order Rp : {{30}}\n5. Sales Invoice # : {{31}}\n6. Sales Invoice Rp : {{32}}\n7. Payment Received # : {{33}}\n8. Payment Received Rp : {{34}}\n9. Return # : {{35}}\n10. Return Rp : {{36}}\n11. DO Cancel # : {{37}}\n12. DO Cancel Rp : {{38}}\n\n*Semua yg sudah di konfirmasi*\n**Payment Received is Paid (not \"in payment\")", "example" => [ "body_text" => [$bodyValues] ] ] ] ]; $jsonPayload = json_encode($data, JSON_PRETTY_PRINT); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', "Authorization: Bearer $token" ], CURLOPT_POSTFIELDS => $jsonPayload, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($httpCode === 401) { $token = getAccessToken(); if ($token) { sendWhatsappAlert($token, [$phone], $bodyValues); // Retry once return; } } if ($error) { echo "cURL Error: $error\n"; } else { echo "HTTP Status: $httpCode\n"; echo "API Response:\n$response\n"; } echo "--------------------------\n"; } } // ========== Odoo API Caller ========== function callOdooAPI($endpoint) { $ODOO_API_TOKEN = "4229879cfa7d264f4d256277d2ee8c2f1d5fe4ea"; $curl = curl_init(); curl_setopt_array($curl, [ CURLOPT_URL => $endpoint, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => '{}', CURLOPT_HTTPHEADER => [ "Authorization: Bearer $ODOO_API_TOKEN", "Content-Type: application/json" ], ]); $response = curl_exec($curl); $error = curl_error($curl); curl_close($curl); if ($error) { echo "❌ Odoo API Error: $error\n"; return null; } return json_decode($response, true); } // ========== Compose & Send ========== // 1. Call Odoo walist API $odooData = callOdooAPI("https://freshfactory.odoo.com/api/sale/walist"); if (!$odooData || !isset($odooData['result']['data_so'])) { echo "❌ Failed to get data from Odoo API\n"; exit; } // 2. Map Odoo response to KKS/KKD/KKR $mapping = [ "PT. Karya Kirana Sempurna" => "kks", "PT. Karya Kirana Distribusi" => "kkd", "PT. Gudang Segar Indonesia" => "kkr" ]; $stats = [ "kks" => [ "so_count" => 0, "so_rp" => 0, "do_count" => 0, "do_rp" => 0, "inv_count" => 0, "inv_rp" => 0, "pay_count" => 0, "pay_rp" => 0, "ret_count" => 0, "ret_rp" => 0, "cancel_count" => 0, "cancel_rp" => 0 ], "kkd" => [ "so_count" => 0, "so_rp" => 0, "do_count" => 0, "do_rp" => 0, "inv_count" => 0, "inv_rp" => 0, "pay_count" => 0, "pay_rp" => 0, "ret_count" => 0, "ret_rp" => 0, "cancel_count" => 0, "cancel_rp" => 0 ], "kkr" => [ "so_count" => 0, "so_rp" => 0, "do_count" => 0, "do_rp" => 0, "inv_count" => 0, "inv_rp" => 0, "pay_count" => 0, "pay_rp" => 0, "ret_count" => 0, "ret_rp" => 0, "cancel_count" => 0, "cancel_rp" => 0 ] ]; foreach ($odooData['result']['data_so'] as $row) { $key = $mapping[$row['name']] ?? null; if ($key) { $stats[$key] = [ "so_count" => $row['so_count'] ?? 0, "so_rp" => $row['so_amount'] ?? 0, "do_count" => $row['do_count'] ?? 0, "do_rp" => $row['do_amount'] ?? 0, "inv_count" => $row['si_count'] ?? 0, "inv_rp" => $row['si_amount'] ?? 0, "pay_count" => $row['pay_count'] ?? 0, "pay_rp" => $row['pay_amount'] ?? 0, "ret_count" => $row['ret_count'] ?? 0, "ret_rp" => $row['ret_amount'] ?? 0, "cancel_count" => $row['cancel_count'] ?? 0, // default if not provided "cancel_rp" => $row['cancel_amount'] ?? 0 ]; } } // 3. Indonesian date formatter function formatIndoShortDate(DateTime $date): string { $monthShort = [ 1 => 'Jan', 2 => 'Feb', 3 => 'Mar', 4 => 'Apr', 5 => 'Mei', 6 => 'Jun', 7 => 'Jul', 8 => 'Agt', 9 => 'Sep', 10 => 'Okt', 11 => 'Nov', 12 => 'Des', ]; $day = $date->format('d'); $month = $monthShort[(int)$date->format('m')]; $year = $date->format('Y'); return "$day $month $year"; } // 4. Get yesterday date $jakartaTz = new DateTimeZone('Asia/Jakarta'); $yesterdayDate = new DateTime('yesterday', $jakartaTz); $yesterday = formatIndoShortDate($yesterdayDate); // 5. Source label $sourceData = "(berdasarkan Odoo)"; // 6. Number formatter $formatter = fn($v) => is_numeric($v) ? number_format((int)$v, 0, ',', '.') : $v; // 6b. Human-readable Rupiah formatter function formatRupiahShort($value) { if (!is_numeric($value)) return $value; if ($value >= 1000000000) { // 1 Miliar return round($value / 1000000000, 1) . ' M'; } elseif ($value >= 1000000) { // 1 Juta return round($value / 1000000, 1) . ' jt'; } elseif ($value >= 1000) { // 1 Ribu return round($value / 1000, 1) . ' rb'; } return (string) $value; } // 7. Build WhatsApp template values $bodyValues = [ $yesterday, // {{1}} $sourceData, // {{2}} // --- KKS (12 values) $formatter($stats["kks"]["so_count"]), // {{3}} formatRupiahShort($stats["kks"]["so_rp"]), // {{4}} $formatter($stats["kks"]["do_count"]), // {{5}} formatRupiahShort($stats["kks"]["do_rp"]), // {{6}} $formatter($stats["kks"]["inv_count"]), // {{7}} formatRupiahShort($stats["kks"]["inv_rp"]), // {{8}} $formatter($stats["kks"]["pay_count"]), // {{9}} formatRupiahShort($stats["kks"]["pay_rp"]), // {{10}} $formatter($stats["kks"]["ret_count"]), // {{11}} formatRupiahShort($stats["kks"]["ret_rp"]), // {{12}} $formatter($stats["kks"]["cancel_count"]), // {{13}} formatRupiahShort($stats["kks"]["cancel_rp"]), // {{14}} // --- KKD (12 values) $formatter($stats["kkd"]["so_count"]), formatRupiahShort($stats["kkd"]["so_rp"]), $formatter($stats["kkd"]["do_count"]), formatRupiahShort($stats["kkd"]["do_rp"]), $formatter($stats["kkd"]["inv_count"]), formatRupiahShort($stats["kkd"]["inv_rp"]), $formatter($stats["kkd"]["pay_count"]), formatRupiahShort($stats["kkd"]["pay_rp"]), $formatter($stats["kkd"]["ret_count"]), formatRupiahShort($stats["kkd"]["ret_rp"]), $formatter($stats["kkd"]["cancel_count"]), formatRupiahShort($stats["kkd"]["cancel_rp"]), // --- KKR (12 values) $formatter($stats["kkr"]["so_count"]), formatRupiahShort($stats["kkr"]["so_rp"]), $formatter($stats["kkr"]["do_count"]), formatRupiahShort($stats["kkr"]["do_rp"]), $formatter($stats["kkr"]["inv_count"]), formatRupiahShort($stats["kkr"]["inv_rp"]), $formatter($stats["kkr"]["pay_count"]), formatRupiahShort($stats["kkr"]["pay_rp"]), $formatter($stats["kkr"]["ret_count"]), formatRupiahShort($stats["kkr"]["ret_rp"]), $formatter($stats["kkr"]["cancel_count"]), formatRupiahShort($stats["kkr"]["cancel_rp"]) ]; // 8. Recipients $recipients = [ //"6281806427245", "628170151579", "628119785008", "6281310755777", "6281233334496", "628111782212", "6281281032178", "6281215566354", "6281345924155", "6282149411130" ]; // 9. Send to WhatsApp $token = getAccessToken(); if ($token) { sendWhatsappAlert($token, $recipients, $bodyValues); } else { echo "Failed to retrieve access token.\n"; }