Files
musadaq-saas/app/core/AI.php
2026-05-04 14:40:41 +03:00

129 lines
4.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Core;
/**
* Gemini AI Integration for Invoice Extraction
*/
class AI
{
private static string $baseUrl = "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent";
/**
* Extract Data from Invoice Image or PDF (Base64)
*/
public static function extractInvoiceData(string $base64Data, string $mimeType): ?array
{
$apiKey = env('GEMINI_API_KEY');
if (!$apiKey) {
error_log('AI Error: GEMINI_API_KEY is missing');
return null;
}
$prompt = "أنت نظام متخصص في استخلاص بيانات الفواتير التجارية. مهمتك واحدة فقط: استخراج البيانات من الفاتورة المرفقة بدقة تامة.
## قواعد صارمة:
**اللغة:**
- إذا كانت الفاتورة بالعربية: أبقِ جميع أسماء السلع والعناوين بالعربية بدون ترجمة
- إذا كانت بالإنجليزية: أبقِها بالإنجليزية بدون ترجمة
- الأرقام دائماً بالأرقام اللاتينية (0-9) بغض النظر عن لغة الفاتورة
- المبالغ بـ 3 أرقام عشرية (مثال: 15.000 وليس 15)
**الدقة:**
- لا تخترع أي بيانات غير موجودة في الفاتورة — أعد null إذا لم تجد المعلومة
- تحقق رياضياً: subtotal = مجموع (quantity × unit_price - discount) لكل سطر
- تحقق: grand_total = subtotal - discount_total + tax_amount
- إذا وجدت تناقضاً بين الأرقام في الفاتورة، سجِّله في حقل \"validation_warnings\"
**الضريبة:**
- في الأردن: ضريبة المبيعات العامة (GST) = 16% للسلع العامة
- سلع معفاة من الضريبة: المواد الغذائية الأساسية، الأدوية، الكتب، بعض المعدات الطبية
- سلع بضريبة مخفضة: قد تكون 4% أو 8% — استخرج النسبة الفعلية من الفاتورة
- لكل سطر: حدد tax_rate الفعلي (0 للمعفاة، وإلا النسبة المئوية كعدد عشري مثل 0.16)
## البيانات المطلوبة (JSON فقط، بدون أي نص إضافي):
```json
{
\"invoice_number\": \"string | null\",
\"invoice_date\": \"YYYY-MM-DD | null\",
\"invoice_type\": \"cash | credit\",
\"payment_method_code\": \"013 | 010 | 001\",
\"supplier\": {
\"name\": \"string | null\",
\"tin\": \"string | null\",
\"address\": \"string | null\"
},
\"buyer\": {
\"name\": \"string | null\",
\"tin\": \"string | null\",
\"national_id\": \"string | null\"
},
\"lines\": [
{
\"line_number\": 1,
\"description\": \"string\",
\"quantity\": 0.000,
\"unit_price\": 0.000,
\"discount\": 0.000,
\"tax_rate\": 0.16,
\"tax_exempt_reason\": \"string | null\",
\"line_total\": 0.000
}
],
\"subtotal\": 0.000,
\"discount_total\": 0.000,
\"tax_amount\": 0.000,
\"grand_total\": 0.000,
\"currency_code\": \"JOD\",
\"math_verified\": true,
\"validation_warnings\": [],
\"ai_confidence\": 0.95
}
```
أعد JSON فقط بدون أي شرح أو مقدمة أو علامات Markdown.";
$payload = [
"contents" => [
[
"parts" => [
["text" => $prompt],
[
"inline_data" => [
"mime_type" => $mimeType,
"data" => $base64Data
]
]
]
]
],
"generationConfig" => [
"response_mime_type" => "application/json"
]
];
$ch = curl_init(self::$baseUrl . "?key=" . $apiKey);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
error_log("AI Error: Gemini API returned code $httpCode. Response: " . $response);
return null;
}
$result = json_decode($response, true);
$textResponse = $result['candidates'][0]['content']['parts'][0]['text'] ?? null;
if (!$textResponse) return null;
return json_decode($textResponse, true);
}
}