'required', ]); if ($errors) { json_error('رقم الشركة مطلوب', 422, $errors); } $companyId = $data['company_id']; $source = $data['source'] ?? 'mobile_scan'; // The mobile client sends 'total_images'; older/web callers send 'expected_images'. // Accept both so the expected count is never silently zero. $expectedImages = (int)($data['expected_images'] ?? $data['total_images'] ?? 0); // 2. Permission check $db = Database::getInstance(); $stmt = $db->prepare("SELECT id, tenant_id FROM companies WHERE id = ? AND deleted_at IS NULL"); $stmt->execute([$companyId]); $company = $stmt->fetch(); if (!$company) { json_error('الشركة غير موجودة', 404); } // Check tenant match if not super_admin if ($decoded['role'] !== 'super_admin' && $company['tenant_id'] !== $tenantId) { json_error('الوصول مرفوض لهذه الشركة', 403); } // Use the actual tenant of the company $targetTenantId = $company['tenant_id']; // 3. Reserve quota for the WHOLE batch up front, not just one invoice. // checkInvoiceQuota() responds and exits by itself when there is no room, so the // old try/catch wrapper never actually caught anything. if ($decoded['role'] !== 'super_admin') { QuotaMiddleware::checkInvoiceQuota($targetTenantId, max(1, $expectedImages)); } // 4. Generate batch ID $batchId = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4)); // 5. Create batch record. // total_images starts at 0 and is incremented by upload-image for each file that // actually lands. Seeding it with the client's expected count here would double // count and break the (processed + failed) >= total completion check. $stmt = $db->prepare(" INSERT INTO invoice_batches (id, tenant_id, company_id, uploaded_by, total_images, source, status) VALUES (?, ?, ?, ?, 0, ?, 'uploading') "); $stmt->execute([$batchId, $targetTenantId, $companyId, $userId, $source]); // 6. Create upload directory $uploadDir = STORAGE_PATH . '/invoices/' . $targetTenantId . '/' . $companyId . '/batches/' . $batchId; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0755, true); } json_success([ 'batch_id' => $batchId, 'upload_url' => 'v1/batches/upload-image', ], 'تم إنشاء الدفعة بنجاح. ابدأ برفع الصور.');