Files
musadaq-saas/app/core/AI.php
2026-05-04 16:06:15 +03:00

116 lines
4.5 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
* Optimized for Jordan UBL 2.1 Compliance
*/
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 = "أنت نظام خبير في استخراج البيانات الضريبية للفواتير في الأردن.
يجب أن تلتزم بالقواعد التالية بصرامة حسابية مطلقة.
### 1. القواعد الحسابية الصارمة (إلزامي):
يجب أن توازن الفاتورة حسابياً قبل إرجاع النتيجة. المعادلة الأساسية هي:
`Grand Total = Subtotal - Discount Total + Tax Amount`
**مثال للتوضيح:**
إذا كانت البنود هي:
1. صنف أ: 12.000 (1 × 12.000)
2. صنف ب: 175.000 (35 × 5.000)
فإن المجموع الفرعي (Subtotal) هو 187.000.
إذا كانت الضريبة 16% على صنف أ فقط، فإن Tax Amount = 1.920.
إذاً الإجمالي (Grand Total) يجب أن يكون 188.920.
**تنبيه:** إذا وجدت في الفاتورة رقماً مكتوباً كإجمالي (Grand Total) ولكنه لا يطابق مجموع البنود والضريبة، قم بتصحيح البيانات المستخرجة للبنود لتتوافق مع المجموع الصحيح أو اتبع المجموع الرياضي الأدق. لا تخرج إجمالياً (مثلاً 15.000) بينما مجموع البنود (311.000).
### 2. قواعد استخراج البيانات:
- **اللغة:** لا تترجم. إذا كان الوصف 'صنف أول' أبقه 'صنف أول'.
- **الأرقام:** استخدم الأرقام اللاتينية (0-9).
- **الدقة:** استخدم 3 أرقام عشرية للمبالغ (مثال: 0.500).
- **الضريبة:** في الأردن، الضريبة العامة هي 16% (0.160). حدد لكل بند النسبة الفعلية.
### 3. هيكل البيانات (JSON فقط):
{
\"invoice_number\": \"string\",
\"invoice_date\": \"YYYY-MM-DD\",
\"invoice_type\": \"cash | credit\",
\"invoice_category\": \"simplified | standard\",
\"supplier\": { \"name\": \"string\", \"tin\": \"string\", \"address\": \"string\" },
\"buyer\": { \"name\": \"string\", \"tin\": \"string\", \"national_id\": \"string\" },
\"lines\": [
{
\"line_number\": 1,
\"description\": \"string\",
\"quantity\": 0.000,
\"unit_price\": 0.000,
\"tax_rate\": 0.160,
\"line_total\": 0.000
}
],
\"subtotal\": 0.000,
\"discount_total\": 0.000,
\"tax_amount\": 0.000,
\"grand_total\": 0.000,
\"currency_code\": \"JOD\"
}
أعد كود 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);
}
}