#!/usr/bin/env php8.4 true, CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => [ trim(explode("\r\n", $auth)[0]), trim(explode("\r\n", $auth)[1]), 'Content-Type: application/json', ], CURLOPT_TIMEOUT => 30, ]); if ($body !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body)); } $response = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($response === false) { return ['status' => 0, 'body' => $error]; } $json = json_decode($response, true); return ['status' => $status, 'body' => $response, 'json' => $json]; } $draftInvoiceNumber = null; $pass = 0; $fail = 0; $total = 0; function check(string $name, bool $ok, string $detail = ''): void { global $pass, $fail, $total; $total++; if ($ok) { $pass++; echo " ✅ $name\n"; if ($detail) echo " $detail\n"; } else { $fail++; echo " ❌ $name\n"; if ($detail) echo " $detail\n"; } } echo "=== E-conomic Live Draft Verification ===\n"; echo "Customer: $customer\n"; echo "API base: $baseUrl\n\n"; try { // ------------------------------------------------------------------ // Step 1: Verify customer exists // ------------------------------------------------------------------ echo "Step 1: Verify customer $customer exists...\n"; $resp = econ_request('GET', "$baseUrl/customers/$customer"); check('Customer exists', $resp['status'] === 200, "HTTP {$resp['status']}"); if ($resp['status'] !== 200) { echo "Cannot proceed without valid customer. Body: " . substr($resp['body'], 0, 200) . "\n"; exit(1); } $customerName = $resp['json']['name'] ?? 'unknown'; echo " Customer name: $customerName\n\n"; // ------------------------------------------------------------------ // Step 2: Create draft invoice // ------------------------------------------------------------------ echo "Step 2: Create draft invoice for customer $customer...\n"; $resp = econ_request('POST', "$baseUrl/invoices/drafts", [ 'currency' => 'DKK', 'customer' => ['customerNumber' => $customer], 'paymentTerms' => ['paymentTermsNumber' => 1], 'layout' => ['layoutNumber' => 1], 'recipient' => ['name' => 'OpenClaw Live Verification'], 'notes' => ['heading' => 'Live verification', 'textLine1' => 'Created by verify-economic-drafts-live.php', 'textLine2' => 'Will be deleted automatically'], ]); check('Draft invoice created', $resp['status'] === 201, "HTTP {$resp['status']}"); if ($resp['status'] !== 201) { echo "Cannot create draft. Body: " . substr($resp['body'], 0, 300) . "\n"; exit(1); } $draftInvoiceNumber = $resp['json']['draftInvoiceNumber'] ?? null; echo " Draft invoice number: $draftInvoiceNumber\n\n"; if (!$draftInvoiceNumber) { echo "No draftInvoiceNumber returned. Body: " . substr($resp['body'], 0, 300) . "\n"; exit(1); } // ------------------------------------------------------------------ // Step 3: Add test lines to draft // ------------------------------------------------------------------ echo "Step 3: Add 2 product lines (1 with discount, 1 without)...\n"; $lines = [ [ 'product' => ['productNumber' => 'OPENCLAW-TEST-01'], 'quantity' => 1.0, 'unitNetPrice' => 100.00, 'discountPercentage' => 0.0, 'description' => 'Test line 1: no discount (verify-economic-drafts-live.php)', ], [ 'product' => ['productNumber' => 'OPENCLAW-TEST-02'], 'quantity' => 2.0, 'unitNetPrice' => 200.00, 'discountPercentage' => 15.0, 'description' => 'Test line 2: 15% discount (verify-economic-drafts-live.php)', ], ]; $resp = econ_request('POST', "$baseUrl/invoices/drafts/$draftInvoiceNumber/lines", [ 'lines' => $lines, ]); check('Lines added to draft', $resp['status'] === 200, "HTTP {$resp['status']}, " . count($lines) . " lines"); // ------------------------------------------------------------------ // Step 4: Verify draft contents // ------------------------------------------------------------------ echo "\nStep 4: Verify draft contents...\n"; $resp = econ_request('GET', "$baseUrl/invoices/drafts/$draftInvoiceNumber"); $draft = $resp['json'] ?? []; $draftLines = $draft['lines'] ?? []; check('Draft has 2 lines', count($draftLines) === 2, 'found ' . count($draftLines)); check('Customer is 12345679', ($draft['customer']['customerNumber'] ?? 0) === $customer); check('Line 1 has 0% discount', abs(($draftLines[0]['discountPercentage'] ?? -1)) < 0.01); check('Line 2 has 15% discount', abs(($draftLines[1]['discountPercentage'] ?? -1) - 15.0) < 0.01); // ------------------------------------------------------------------ // Step 5: Cleanup - delete the draft // ------------------------------------------------------------------ echo "\nStep 5: Cleanup - delete draft $draftInvoiceNumber...\n"; $resp = econ_request('DELETE', "$baseUrl/invoices/drafts/$draftInvoiceNumber"); check('Draft deleted', $resp['status'] === 204 || $resp['status'] === 200, "HTTP {$resp['status']}"); if ($resp['status'] !== 204 && $resp['status'] !== 200) { echo "\n⚠️ WARNING: Cleanup failed. Draft $draftInvoiceNumber still exists in e-conomic.\n"; echo " Delete it manually: curl -X DELETE -H \"$auth\" $baseUrl/invoices/drafts/$draftInvoiceNumber\n"; exit(2); } // ------------------------------------------------------------------ // Step 6: Verify deletion // ------------------------------------------------------------------ echo "\nStep 6: Verify draft is gone...\n"; $resp = econ_request('GET', "$baseUrl/invoices/drafts/$draftInvoiceNumber"); check('Draft no longer exists', $resp['status'] === 404, "HTTP {$resp['status']} (expected 404)"); } catch (\Throwable $e) { echo "\n💥 UNCAUGHT ERROR: " . $e->getMessage() . "\n"; echo "Stack trace:\n" . $e->getTraceAsString() . "\n"; // Best-effort cleanup if ($draftInvoiceNumber !== null) { echo "\nAttempting emergency cleanup of draft $draftInvoiceNumber...\n"; $resp = econ_request('DELETE', "$baseUrl/invoices/drafts/$draftInvoiceNumber"); echo " Cleanup HTTP status: {$resp['status']}\n"; if ($resp['status'] !== 204 && $resp['status'] !== 200) { echo " ⚠️ MANUAL CLEANUP REQUIRED: DELETE $baseUrl/invoices/drafts/$draftInvoiceNumber\n"; exit(2); } } exit(1); } echo "\n=== Summary: $pass/$total checks passed ===\n"; exit($fail === 0 ? 0 : 1);