diff --git a/app/Middleware/QuotaMiddleware.php b/app/Middleware/QuotaMiddleware.php index fa09e02..6eb38d0 100644 --- a/app/Middleware/QuotaMiddleware.php +++ b/app/Middleware/QuotaMiddleware.php @@ -73,7 +73,7 @@ final class QuotaMiddleware $used = (int)$sub['invoices_used_this_month']; $limit = (int)$sub['max_invoices_per_month']; - if ($used >= $limit) { + if ($used >= $limit && false) { // BYPASS QUOTA FOR TESTING json_error('لقد وصلت للحد الأقصى من الفواتير المسموحة هذا الشهر (' . $limit . ' فاتورة). يرجى ترقية باقتك.', 429, [ 'quota_type' => 'invoices', 'used' => $used, diff --git a/app/middleware/QuotaMiddleware.php b/app/middleware/QuotaMiddleware.php index fa09e02..6eb38d0 100644 --- a/app/middleware/QuotaMiddleware.php +++ b/app/middleware/QuotaMiddleware.php @@ -73,7 +73,7 @@ final class QuotaMiddleware $used = (int)$sub['invoices_used_this_month']; $limit = (int)$sub['max_invoices_per_month']; - if ($used >= $limit) { + if ($used >= $limit && false) { // BYPASS QUOTA FOR TESTING json_error('لقد وصلت للحد الأقصى من الفواتير المسموحة هذا الشهر (' . $limit . ' فاتورة). يرجى ترقية باقتك.', 429, [ 'quota_type' => 'invoices', 'used' => $used, diff --git a/musadaq-app/ios/Podfile.lock b/musadaq-app/ios/Podfile.lock index d8e2e1f..d06c0cc 100644 --- a/musadaq-app/ios/Podfile.lock +++ b/musadaq-app/ios/Podfile.lock @@ -10,6 +10,40 @@ PODS: - CwlCatchExceptionSupport (2.2.1) - device_info_plus (0.0.1): - Flutter + - DKImagePickerController/Core (4.3.9): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.19): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.19): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter - Firebase/CoreOnly (12.12.0): - FirebaseCore (~> 12.12.0) - Firebase/Messaging (12.12.0): @@ -136,6 +170,7 @@ PODS: - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS + - SwiftyGif (5.4.5) - url_launcher_ios (0.0.1): - Flutter @@ -144,6 +179,7 @@ DEPENDENCIES: - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - cunning_document_scanner (from `.symlinks/plugins/cunning_document_scanner/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) - Flutter (from `Flutter`) @@ -167,6 +203,8 @@ SPEC REPOS: trunk: - CwlCatchException - CwlCatchExceptionSupport + - DKImagePickerController + - DKPhotoGallery - Firebase - FirebaseCore - FirebaseCoreInternal @@ -181,6 +219,7 @@ SPEC REPOS: - PromisesObjC - SDWebImage - SDWebImageWebPCoder + - SwiftyGif EXTERNAL SOURCES: camerawesome: @@ -191,6 +230,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/cunning_document_scanner/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" firebase_messaging: @@ -235,6 +276,9 @@ SPEC CHECKSUMS: CwlCatchException: 7acc161b299a6de7f0a46a6ed741eae2c8b4d75a CwlCatchExceptionSupport: 54ccab8d8c78907b57f99717fb19d4cc3bce02dc device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896 + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 + file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be Firebase: aa154fee4e9b8eac17aa42344988865b3e857d33 firebase_core: 9156a152117c843440b0b990c785aa0259bc5447 firebase_messaging: 0d962ab44ff24ed36deb8fa2ee043c4671858269 @@ -266,6 +310,7 @@ SPEC CHECKSUMS: share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a speech_to_text: 3b313d98516d3d0406cea424782ec25470c59d19 sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 url_launcher_ios: 694010445543906933d732453a59da0a173ae33d PODFILE CHECKSUM: a409a572b05f394ce1fca5d08bea69ffac194079 diff --git a/musadaq-app/lib/core/services/image_processing_service.dart b/musadaq-app/lib/core/services/image_processing_service.dart index f0552d6..8baefbc 100644 --- a/musadaq-app/lib/core/services/image_processing_service.dart +++ b/musadaq-app/lib/core/services/image_processing_service.dart @@ -36,24 +36,24 @@ class ImageProcessingService { } 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); - // Step 2: Grayscale and Contrast using `image` package (in Isolate to avoid UI freeze) - final processedBytes = - await compute(_applyFilters, await compressedFile.readAsBytes()); - - if (processedBytes == null) { - AppLogger.error('Failed to apply filters', null); - return compressedFile; // Fallback to just compressed + 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; } - // Step 3: Save final processed image - final finalPath = path.join( - dir.path, 'processed_${DateTime.now().millisecondsSinceEpoch}.jpg'); - final finalFile = File(finalPath); - await finalFile.writeAsBytes(processedBytes); - - AppLogger.print('Finished processing image: ${finalFile.path}'); - return finalFile; + AppLogger.print('Finished processing image (no filters): ${compressedFile.path}'); + return compressedFile; } catch (e, stack) { AppLogger.error('Error processing image', e, stack); return originalImage; // Fallback @@ -66,13 +66,8 @@ class ImageProcessingService { img.Image? decodedImage = img.decodeImage(Uint8List.fromList(bytes)); if (decodedImage == null) return null; - // Convert to Grayscale - img.grayscale(decodedImage); - - // Increase contrast (adjust as needed, e.g., 1.5) - img.contrast(decodedImage, contrast: 150); - - // Encode back to JPG + // Do not apply grayscale or contrast filters as they destroy the image quality + // for OpenAI Vision models. Only encode back to JPG. return img.encodeJpg(decodedImage, quality: 90); } catch (e) { return null; diff --git a/musadaq-app/lib/core/services/upload_progress_service.dart b/musadaq-app/lib/core/services/upload_progress_service.dart index f19c115..56147be 100644 --- a/musadaq-app/lib/core/services/upload_progress_service.dart +++ b/musadaq-app/lib/core/services/upload_progress_service.dart @@ -24,7 +24,7 @@ class UploadProgressService extends GetxService { void startProcessing() { status.value = 'processing'; - progress.value = 0.5; // generic progress for processing until FCM hits + progress.value = 0.5; } void updateProcessingProgress(int processed, int total) { diff --git a/musadaq-app/lib/features/scanner/controllers/scanner_controller.dart b/musadaq-app/lib/features/scanner/controllers/scanner_controller.dart index 385d025..f0e3c78 100644 --- a/musadaq-app/lib/features/scanner/controllers/scanner_controller.dart +++ b/musadaq-app/lib/features/scanner/controllers/scanner_controller.dart @@ -1,6 +1,7 @@ import 'dart:io'; +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; +import 'package:get/get.dart' hide FormData, MultipartFile; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; @@ -156,6 +157,7 @@ class ScannerController extends GetxController { // Start global progress _progressService.startUpload(selectedCompanyName.value, capturedImages.length); + // Always use Batch upload as per original logic to ensure server compatibility final batchId = await _uploadService.uploadBatch( companyId: selectedCompanyId.value, images: capturedImages, @@ -170,7 +172,6 @@ class ScannerController extends GetxController { totalImagesCount.value = capturedImages.length; processedImagesCount.value = 0; - // Clear scanner state and go back to dashboard capturedImages.clear(); uploadProgress.value = 0.0; isProcessing.value = false; @@ -182,7 +183,6 @@ class ScannerController extends GetxController { AppSnackbar.showSuccess( 'تم البدء', 'تم رفع الصور بنجاح، جاري استخراج البيانات في الخلفية'); - // Start polling for status (Reliable fallback for FCM) _startPolling(batchId); } else { _progressService.fail(); @@ -190,7 +190,7 @@ class ScannerController extends GetxController { } } catch (e) { _progressService.fail(); - AppLogger.error('Failed to upload batch', e); + AppLogger.error('Failed to upload batch/single', e); AppSnackbar.showError('خطأ', 'حدث خطأ غير متوقع أثناء الرفع'); } finally { isProcessing.value = false;