user('web'); abort_unless($actor instanceof User, 403); $view = $request->validate(['view' => 'required|in:reservations,deliveries,alternatives'])['view']; $kind = $read->kind($actor, $view); $filters = $read->filters($request, $kind); abort_unless(Schema::hasTable('supply_records'), 503, 'Instalação do fornecimento pendente.'); // Validate the same focus as the page, without silently exporting other records. $focus = $request->validate(['record_id' => ['nullable', 'integer', 'min:1', Rule::exists('supply_records', 'id')->where('tenant_id', $actor->tenant_id)->where('kind', $kind)]]); return response()->streamDownload(function () use ($read, $actor, $view, $kind, $filters, $focus) { DB::transaction(function () use ($read, $actor, $view, $kind, $filters, $focus) { if (DB::getDriverName() === 'pgsql' && DB::transactionLevel() === 1) { DB::statement('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY'); } // Recheck access when the response is consumed. $read->kind($actor, $view); $query = $read->query($kind, isset($focus['record_id']) ? ['record_search' => '', 'record_state' => ''] : $filters); if (isset($focus['record_id'])) { $key = SupplyRecord::query()->whereKey($focus['record_id'])->value('record_key'); $query->where('record_key', $key); } $out = fopen('php://output', 'w'); if ($out === false) { throw new \RuntimeException('Não foi possível preparar a exportação.'); } $text = static function (mixed $value): string { $value = (string) ($value ?? ''); return preg_match('/^[\s\x00-\x1F]*[=+\-@]/u', $value) ? "'".$value : $value; }; try { fwrite($out, "\xEF\xBB\xBF"); fputcsv($out, ['Acompanhamento de fornecimentos', $view, 'Conferido em', now()->toIso8601String()], ';', '"', ''); fputcsv($out, ['Filtros', $text(json_encode($filters, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)), 'Registo em foco', $focus['record_id'] ?? ''], ';', '"', ''); fputcsv($out, ['Registo', 'Versão', 'Estado registado', 'Artigo', 'Designação preservada', 'Local preservado', 'Referência', 'Encomenda', 'Quantidade', 'Unidade', 'Data prevista/necessidade', 'Quantidade por satisfazer (ver estado)', 'Substituto', 'Aplicação', 'Validade', 'Processo'], ';', '"', ''); foreach ($query->lazyById(100) as $record) { $pending = match ($kind) { 'reservation' => app(SupplyCoordination::class)->remainingReservation($record), 'delivery' => app(SupplyCoordination::class)->remainingDelivery($record), default => null, }; fputcsv($out, array_map($text, [$record->record_key, $record->version, $record->state, $record->snapshot['item']['code'] ?? '', $record->snapshot['item']['name'] ?? '', $record->snapshot['warehouse']['name'] ?? '', $record->data['reference'] ?? '', $record->snapshot['order']['number'] ?? '', isset($record->data['quantity_milli']) ? $record->data['quantity_milli'] / 1000 : '', $record->snapshot['item']['unit'] ?? '', $record->data['needed_on'] ?? $record->data['expected_on'] ?? '', $pending === null ? '' : $pending / 1000, $record->snapshot['substitute']['code'] ?? '', $record->data['application'] ?? '', $record->data['valid_until'] ?? '', url('/compras/fornecimento?view='.$view.'&record_id='.$record->id).'#supply-record-'.$record->id]), ';', '"', ''); } } finally { fclose($out); } }); }, 'fornecimentos-'.$view.'-'.now()->format('Ymd-His').'.csv', ['Content-Type' => 'text/csv; charset=UTF-8', 'Cache-Control' => 'private, no-store, max-age=0']); } }