setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); echo "=== BEFORE FIX ===\n"; $stmt = $pdo->query("SELECT job_type, status, COUNT(*) as cnt FROM pp_cron_jobs WHERE status IN ('pending','processing','failed') GROUP BY job_type, status ORDER BY job_type"); foreach ($stmt as $row) { echo " {$row['job_type']} {$row['status']} {$row['cnt']}\n"; } // 1. Reset the stuck apilo_send_order job (attempts back to 0, clear error) echo "\n=== Resetting apilo_send_order pending jobs ===\n"; $stmt = $pdo->prepare("UPDATE pp_cron_jobs SET attempts = 0, last_error = NULL, scheduled_at = NOW() WHERE job_type = 'apilo_send_order' AND status = 'pending'"); $stmt->execute(); echo " Reset {$stmt->rowCount()} apilo_send_order jobs\n"; // 2. Reset token keepalive failed jobs echo "\n=== Resetting apilo_token_keepalive failed jobs ===\n"; $stmt = $pdo->prepare("UPDATE pp_cron_jobs SET attempts = 0, last_error = NULL, status = 'pending', scheduled_at = NOW() WHERE job_type = 'apilo_token_keepalive' AND status = 'failed'"); $stmt->execute(); echo " Reset {$stmt->rowCount()} token keepalive jobs\n"; // 3. Cancel sync_payment and sync_status jobs for orders that haven't been sent yet // (these orders have apilo_order_id = NULL so sync is pointless — they'll be re-queued after order is sent) echo "\n=== Cancelling premature sync jobs for unsent orders ===\n"; $stmt = $pdo->query(" SELECT cj.id, cj.job_type, cj.payload FROM pp_cron_jobs cj WHERE cj.status = 'pending' AND cj.job_type IN ('apilo_sync_payment', 'apilo_sync_status') "); $to_cancel = []; foreach ($stmt as $row) { $payload = json_decode($row['payload'], true); $order_id = $payload['order_id'] ?? 0; // Check if this order has been sent to Apilo $check = $pdo->prepare("SELECT apilo_order_id FROM pp_shop_orders WHERE id = ?"); $check->execute([$order_id]); $order = $check->fetch(); if ($order && ($order['apilo_order_id'] === null || (int)$order['apilo_order_id'] <= 0)) { $to_cancel[] = $row['id']; } } if ($to_cancel) { $ids = implode(',', $to_cancel); $pdo->exec("UPDATE pp_cron_jobs SET status = 'cancelled' WHERE id IN ($ids)"); echo " Cancelled " . count($to_cancel) . " premature sync jobs\n"; } else { echo " No premature sync jobs to cancel\n"; } // 4. Reset failed product_sync, pricelist_sync, status_poll jobs echo "\n=== Resetting other failed Apilo jobs ===\n"; $stmt = $pdo->prepare("UPDATE pp_cron_jobs SET attempts = 0, last_error = NULL, scheduled_at = NOW() WHERE job_type IN ('apilo_product_sync', 'apilo_pricelist_sync', 'apilo_status_poll') AND status = 'pending'"); $stmt->execute(); echo " Reset {$stmt->rowCount()} other Apilo pending jobs\n"; echo "\n=== AFTER FIX ===\n"; $stmt = $pdo->query("SELECT job_type, status, COUNT(*) as cnt FROM pp_cron_jobs WHERE status IN ('pending','processing','failed') GROUP BY job_type, status ORDER BY job_type"); foreach ($stmt as $row) { echo " {$row['job_type']} {$row['status']} {$row['cnt']}\n"; } echo "\n=== ORDERS NEEDING SEND (apilo_order_id IS NULL, after sync_start) ===\n"; $stmt = $pdo->query("SELECT COUNT(*) as cnt FROM pp_shop_orders WHERE apilo_order_id IS NULL AND date_order >= '2024-08-20'"); $row = $stmt->fetch(); echo " " . $row['cnt'] . " orders to send\n"; echo " (cron processes 1 per run, should catch up within " . $row['cnt'] . " cron cycles)\n"; echo "\nDone. Deploy fixed cron.php to instance, then run this script.\n";