prepare($query); $stmt->execute($params); $invoice = $stmt->fetch(); if (!$invoice) { json_error('الفاتورة غير موجودة أو ليس لديك صلاحية', 404); } if ($invoice['status'] !== 'approved') { json_error('يجب اعتماد الفاتورة أولاً قبل إرسالها لجوفتورة', 400); } // 2. Check if already submitted $stmtCheck = $db->prepare("SELECT id FROM jofotara_submissions WHERE invoice_id = ? AND status = 'accepted'"); $stmtCheck->execute([$invoiceId]); if ($stmtCheck->fetch()) { json_error('تم إرسال هذه الفاتورة لجوفتورة مسبقاً', 400); } // 3. Verify JoFotara credentials $clientId = Encryption::decrypt($invoice['jofotara_client_id_encrypted'] ?? '') ?: ''; $secretKey = Encryption::decrypt($invoice['jofotara_secret_key_encrypted'] ?? '') ?: ''; if (empty($clientId) || empty($secretKey)) { json_error('يجب ربط الشركة بمنظومة جوفتورة أولاً (Client ID + Secret Key)', 422); } // 4. Decrypt sensitive fields for XML generation $dec = function($val) { if (empty($val)) return ''; $result = Encryption::decrypt((string)$val); return ($result !== false && $result !== null) ? $result : (string)$val; }; // Prepare invoice data for XML $invoiceData = [ 'invoice_number' => $invoice['invoice_number'], 'invoice_date' => $invoice['invoice_date'], 'invoice_type' => $invoice['invoice_type'], 'invoice_category' => $invoice['invoice_category'] ?? 'simplified', 'ubl_type_code' => $invoice['ubl_type_code'] ?? '388', 'payment_method_code' => $invoice['payment_method_code'] ?? '013', 'buyer_name' => $dec($invoice['buyer_name']), 'buyer_tin' => $dec($invoice['buyer_tin']), 'buyer_national_id' => $dec($invoice['buyer_national_id']), 'subtotal' => (float)$invoice['subtotal'], 'tax_amount' => (float)$invoice['tax_amount'], 'discount_total' => (float)$invoice['discount_total'], 'grand_total' => (float)$invoice['grand_total'], ]; // Fetch line items $stmtLines = $db->prepare("SELECT * FROM invoice_lines WHERE invoice_id = ? ORDER BY line_number ASC"); $stmtLines->execute([$invoiceId]); $invoiceData['items'] = $stmtLines->fetchAll(); $companyData = [ 'name' => $invoice['company_name'], 'tax_identification_number' => $invoice['tax_identification_number'], 'address' => $invoice['company_address'] ?? '', ]; // 5. Generate XML $jofotara = new JoFotara(); $xml = $jofotara->generateXML($invoiceData, $companyData); // 6. Submit to JoFotara API $result = $jofotara->submitInvoice($xml, $clientId, $secretKey); // 7. Record submission $submissionId = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4)); $stmt = $db->prepare(" INSERT INTO jofotara_submissions (id, invoice_id, tenant_id, company_id, jofotara_uuid, xml_content, status, qr_code_raw, response_body, submitted_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW()) "); $stmt->execute([ $submissionId, $invoiceId, $tenantId, $invoice['company_id'], $result['uuid'] ?? null, $xml, $result['success'] ? 'accepted' : 'rejected', $result['qrCode'] ?? null, json_encode($result['raw'] ?? [], JSON_UNESCAPED_UNICODE), ]); // 8. Update invoice status if accepted if ($result['success']) { $db->prepare("UPDATE invoices SET status = 'submitted', jofotara_uuid = ? WHERE id = ?") ->execute([$result['uuid'], $invoiceId]); // Generate local QR code $qrBase64 = $jofotara->generateQRCode([ 'supplier_name' => $companyData['name'], 'supplier_tin' => $companyData['tax_identification_number'], 'invoice_date' => $invoiceData['invoice_date'], 'grand_total' => $invoiceData['grand_total'], 'tax_amount' => $invoiceData['tax_amount'], ]); $db->prepare("UPDATE invoices SET qr_code = ? WHERE id = ?")->execute([$qrBase64, $invoiceId]); AuditLogger::log('invoice.submitted_jofotara', 'invoice', $invoiceId, null, [ 'jofotara_uuid' => $result['uuid'], ], $decoded); \App\Services\SmartNotifications::jofotaraSuccess($tenantId, $userId, $invoiceId, $result['uuid']); json_success([ 'uuid' => $result['uuid'], 'qr_code' => $qrBase64, 'status' => 'accepted', ], 'تم إرسال الفاتورة لجوفتورة بنجاح'); } else { AuditLogger::log('invoice.jofotara_rejected', 'invoice', $invoiceId, null, [ 'error' => $result['error'] ?? 'Unknown', ], $decoded); \App\Services\SmartNotifications::jofotaraRejected($tenantId, $userId, $invoiceId, $result['error'] ?? 'خطأ غير محدد'); json_error('رُفضت الفاتورة من جوفتورة: ' . ($result['error'] ?? 'خطأ غير محدد'), 422); }