87 lines
2.8 KiB
PHP
87 lines
2.8 KiB
PHP
<?php
|
|
/**
|
|
* Create Batch Endpoint
|
|
* POST /v1/batches/create
|
|
*
|
|
* Creates a new invoice batch for the mobile scanner.
|
|
* Returns batch_id that the mobile app uses to upload images.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Core\Database;
|
|
use App\Middleware\AuthMiddleware;
|
|
use App\Core\Security;
|
|
use App\Core\Validator;
|
|
use App\Middleware\QuotaMiddleware;
|
|
|
|
$decoded = AuthMiddleware::check();
|
|
$tenantId = $decoded['tenant_id'];
|
|
$userId = $decoded['user_id'];
|
|
|
|
$data = Security::sanitize(input());
|
|
|
|
// 1. Validate
|
|
$errors = Validator::validate($data, [
|
|
'company_id' => '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',
|
|
], 'تم إنشاء الدفعة بنجاح. ابدأ برفع الصور.');
|