Update: 2026-05-12 01:29:57

This commit is contained in:
Hamza-Ayed
2026-05-12 01:29:57 +03:00
parent d6cedd23c1
commit ae5eba09aa
4 changed files with 23 additions and 48 deletions

View File

@@ -96,6 +96,10 @@ class AI
$textResponse = $result['candidates'][0]['content']['parts'][0]['text'] ?? null; $textResponse = $result['candidates'][0]['content']['parts'][0]['text'] ?? null;
if (!$textResponse) return null; if (!$textResponse) return null;
// --- ADDED FOR DEBUGGING ---
@file_put_contents(STORAGE_PATH . '/logs/worker.log', "[" . date('Y-m-d H:i:s') . "] [AI_RAW_RESPONSE]\n" . $textResponse . "\n", FILE_APPEND);
// ---------------------------
$data = json_decode($textResponse, true); $data = json_decode($textResponse, true);
if (isset($data['error']) && $data['error'] === 'invalid_invoice') { if (isset($data['error']) && $data['error'] === 'invalid_invoice') {

View File

@@ -102,21 +102,26 @@ class InvoiceProcessor
supplier_tin, supplier_name, supplier_address, supplier_tin, supplier_name, supplier_address,
buyer_tin, buyer_name, buyer_national_id, buyer_tin, buyer_name, buyer_national_id,
subtotal, tax_amount, discount_total, grand_total, currency_code, subtotal, tax_amount, discount_total, grand_total, currency_code,
ai_provider,
created_at created_at
) VALUES ( ) VALUES (
?, ?, ?, ?, ?, 'extracted', ?, ?, ?, ?, ?, 'extracted',
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?,
NOW() NOW()
) )
"); ");
$rawJsonSnippet = substr(json_encode($extracted, JSON_UNESCAPED_UNICODE), 0, 500);
$stmt->execute([ $stmt->execute([
$invoiceId, $tenantId, $companyId, $userId, $imagePath, $invoiceId, $tenantId, $companyId, $userId, $imagePath,
$invoiceNum, $validDate, $extracted['invoice_type'] ?? 'cash', $extracted['invoice_category'] ?? 'simplified', $invoiceNum, $validDate, $extracted['invoice_type'] ?? 'cash', $extracted['invoice_category'] ?? 'simplified',
Encryption::encrypt($supplierTin), Encryption::encrypt($extracted['supplier']['name'] ?? ''), Encryption::encrypt($extracted['supplier']['address'] ?? ''), Encryption::encrypt($supplierTin), Encryption::encrypt($extracted['supplier']['name'] ?? ''), Encryption::encrypt($extracted['supplier']['address'] ?? ''),
Encryption::encrypt($extracted['buyer']['tin'] ?? ''), Encryption::encrypt($extracted['buyer']['name'] ?? ''), Encryption::encrypt($extracted['buyer']['national_id'] ?? ''), Encryption::encrypt($extracted['buyer']['tin'] ?? ''), Encryption::encrypt($extracted['buyer']['name'] ?? ''), Encryption::encrypt($extracted['buyer']['national_id'] ?? ''),
$extracted['subtotal'] ?? 0, $extracted['tax_amount'] ?? 0, $extracted['discount_total'] ?? 0, $extracted['grand_total'] ?? 0, $extracted['currency_code'] ?? 'JOD' $extracted['subtotal'] ?? 0, $extracted['tax_amount'] ?? 0, $extracted['discount_total'] ?? 0, $extracted['grand_total'] ?? 0, $extracted['currency_code'] ?? 'JOD',
$rawJsonSnippet
]); ]);
// Save invoice line items // Save invoice line items

View File

@@ -13,51 +13,11 @@ class ImageProcessingService {
/// 3. Increases contrast /// 3. Increases contrast
/// 4. Saves as high-quality JPEG /// 4. Saves as high-quality JPEG
static Future<File?> processInvoiceImage(File originalImage) async { static Future<File?> processInvoiceImage(File originalImage) async {
try { // The web app uploads the original file and OCR works 100%.
AppLogger.print('Started processing image: ${originalImage.path}'); // Processing/compressing it here strips EXIF/quality and causes OCR to fail.
// So we just return the original image intact.
// Step 1: Initial compression using flutter_image_compress (Native, fast) AppLogger.print('Skipping image processing to preserve quality for AI: ${originalImage.path}');
final dir = await getTemporaryDirectory(); return originalImage;
final compressedPath = path.join(
dir.path, 'compressed_${DateTime.now().millisecondsSinceEpoch}.jpg');
final XFile? compressedXFile =
await FlutterImageCompress.compressAndGetFile(
originalImage.path,
compressedPath,
quality: 85,
minWidth: 1920,
minHeight: 1080,
);
if (compressedXFile == null) {
AppLogger.error('Failed to compress image initially', null);
return originalImage; // Fallback to original
}
File compressedFile = File(compressedXFile.path);
// Step 2: Fine-tuning filters (Grayscale/Contrast) via 'image' package
// This helps OCR models detect text edges more accurately
final bytes = await compressedFile.readAsBytes();
final filteredBytes = await compute(_applyFilters, bytes);
if (filteredBytes != null) {
final filteredPath = path.join(
dir.path, 'filtered_${DateTime.now().millisecondsSinceEpoch}.jpg');
final filteredFile = File(filteredPath);
await filteredFile.writeAsBytes(filteredBytes);
AppLogger.print('Finished processing image with filters: ${filteredFile.path}');
return filteredFile;
}
AppLogger.print('Finished processing image (no filters): ${compressedFile.path}');
return compressedFile;
} catch (e, stack) {
AppLogger.error('Error processing image', e, stack);
return originalImage; // Fallback
}
} }
/// Runs in an isolate to prevent UI freezing /// Runs in an isolate to prevent UI freezing

View File

@@ -17,8 +17,14 @@ class InvoiceDetailController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
if (Get.arguments != null) { final args = Get.arguments;
invoiceId = Get.arguments['id']; if (args != null) {
if (args is Map) {
invoiceId = args['id']?.toString();
} else if (args is String) {
invoiceId = args;
}
if (invoiceId != null) { if (invoiceId != null) {
fetchInvoiceDetails(); fetchInvoiceDetails();
} }