user()?->hasRole('admin') ?? false; } public static function form(Form $form): Form { return $form->schema([ Forms\Components\Select::make('id_transportadora') ->label('Transportadora') ->relationship('transportadora', 'nome_transportadora') ->required() ->searchable() ->preload(), Forms\Components\TextInput::make('numero_fatura') ->label('Nº Fatura') ->required() ->maxLength(50), Forms\Components\DatePicker::make('data_emissao') ->label('Data Emissão') ->required(), Forms\Components\TextInput::make('valor_total') ->label('Valor Total') ->required() ->numeric() ->prefix('R$'), Forms\Components\DatePicker::make('data_vencimento') ->label('Vencimento') ->required(), ]); } public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('transportadora.nome_transportadora')->label('Transportadora')->searchable(), Tables\Columns\TextColumn::make('numero_fatura')->label('Nº Fatura')->searchable(), Tables\Columns\TextColumn::make('data_emissao')->label('Emissão')->date('d/m/Y')->sortable(), Tables\Columns\TextColumn::make('valor_total')->label('Valor')->money('BRL'), Tables\Columns\TextColumn::make('data_vencimento')->label('Vencimento')->date('d/m/Y')->sortable(), Tables\Columns\TextColumn::make('ctes_count') ->label('CTEs') ->counts('ctes') ->badge() ->color('info'), ]) ->defaultSort('data_emissao', 'desc') ->filters([ Tables\Filters\SelectFilter::make('id_transportadora') ->label('Transportadora') ->relationship('transportadora', 'nome_transportadora') ->searchable() ->preload(), Tables\Filters\Filter::make('periodo') ->form([ Forms\Components\Select::make('periodo_rapido') ->label('PerÃodo') ->options([ 'este_mes' => 'Este mês', 'mes_passado' => 'Mês passado', 'customizado' => 'Customizado', ]) ->reactive(), Forms\Components\DatePicker::make('data_inicio') ->label('De') ->visible(fn ($get) => $get('periodo_rapido') === 'customizado'), Forms\Components\DatePicker::make('data_fim') ->label('Até') ->visible(fn ($get) => $get('periodo_rapido') === 'customizado'), ]) ->query(function ($query, array $data) { $periodo = $data['periodo_rapido'] ?? null; if (!$periodo) return $query; return match ($periodo) { 'este_mes' => $query->whereBetween('data_emissao', [now()->startOfMonth(), now()->endOfMonth()]), 'mes_passado' => $query->whereBetween('data_emissao', [now()->subMonth()->startOfMonth(), now()->subMonth()->endOfMonth()]), 'customizado' => $query ->when($data['data_inicio'] ?? null, fn ($q) => $q->whereDate('data_emissao', '>=', $data['data_inicio'])) ->when($data['data_fim'] ?? null, fn ($q) => $q->whereDate('data_emissao', '<=', $data['data_fim'])), default => $query, }; }), ]) ->filtersFormColumns(2) ->headerActions([ Tables\Actions\Action::make('fechar_fatura') ->label('Fechar Fatura') ->icon('heroicon-o-document-plus') ->color('success') ->form([ Forms\Components\Select::make('id_transportadora') ->label('Transportadora') ->options(fn () => Transportadora::where('ativo', true)->orderBy('nome_transportadora')->pluck('nome_transportadora', 'id_transportadora')->toArray()) ->required() ->searchable() ->live() ->afterStateUpdated(fn (Forms\Set $set) => $set('ctes_selecionados', [])), Forms\Components\FileUpload::make('csv_import') ->label('Importar CSV (opcional)') ->acceptedFileTypes(['text/csv', 'application/vnd.ms-excel', 'text/plain']) ->helperText('Coluna K = NUMERO CT-E. Após upload, clique em "Processar CSV".') ->live() ->visible(fn (Forms\Get $get) => (bool) $get('id_transportadora')), Forms\Components\Actions::make([ Forms\Components\Actions\Action::make('processar_csv') ->label('Processar CSV') ->icon('heroicon-o-arrow-path') ->color('info') ->action(function (Forms\Get $get, Forms\Set $set) { $state = $get('csv_import'); $transportadoraId = $get('id_transportadora'); if (!$state || !$transportadoraId) return; $path = is_array($state) ? collect($state)->first() : $state; if (!$path) return; // Buscar o CSV mais recente no livewire-tmp $fullPath = null; $searchPaths = [ storage_path('app/private/livewire-tmp/' . $path), storage_path('app/private/livewire-tmp/'), ]; if (file_exists($searchPaths[0])) { $fullPath = $searchPaths[0]; } else { $files = glob(storage_path('app/private/livewire-tmp/*.csv')); if (!empty($files)) { usort($files, fn($a, $b) => filemtime($b) - filemtime($a)); $fullPath = $files[0]; } } if (!$fullPath) { Notification::make()->title('Arquivo não encontrado.')->danger()->send(); return; } $numeros = []; if (($handle = fopen($fullPath, 'r')) !== false) { $row = 0; while (($line = fgetcsv($handle, 0, ';')) !== false) { $row++; if ($row <= 2) continue; $numero = trim($line[10] ?? ''); if ($numero && strlen($numero) > 5) { $numeros[] = (string) (int) substr($numero, 5); } } fclose($handle); } if (empty($numeros)) { Notification::make()->title('Nenhum CT-e encontrado no CSV.')->warning()->send(); return; } $transportadora = Transportadora::find($transportadoraId); $nomes = collect([$transportadora->nome_transportadora]) ->merge($transportadora->aliases ?? []) ->filter()->toArray(); $ids = Cte::whereNull('id_fatura') ->whereIn('transportadora', $nomes) ->whereIn('numero_cte', $numeros) ->pluck('id') ->toArray(); $set('ctes_selecionados', $ids); Notification::make()->title(count($ids) . ' CTe(s) selecionado(s) de ' . count($numeros) . ' no CSV.')->success()->send(); }), ])->visible(fn (Forms\Get $get) => !empty($get('csv_import'))), Forms\Components\CheckboxList::make('ctes_selecionados') ->label('CTEs pendentes') ->options(function (Forms\Get $get) { $transportadoraId = $get('id_transportadora'); if (!$transportadoraId) return []; $transportadora = Transportadora::find($transportadoraId); if (!$transportadora) return []; $nomes = collect([$transportadora->nome_transportadora]) ->merge($transportadora->aliases ?? []) ->filter() ->toArray(); return Cte::whereNull('id_fatura') ->whereIn('transportadora', $nomes) ->orderBy('data_emissao', 'desc') ->get() ->mapWithKeys(fn ($cte) => [ $cte->id => "CTe {$cte->numero_cte} — {$cte->destinatario} — R$ " . number_format((float) $cte->valor_frete, 2, ',', '.') . " — " . ($cte->data_emissao?->format('d/m/Y') ?? '-'), ]) ->toArray(); }) ->required() ->searchable() ->bulkToggleable() ->columns(1) ->visible(fn (Forms\Get $get) => (bool) $get('id_transportadora')), Forms\Components\TextInput::make('numero_fatura') ->label('Nº Fatura') ->required() ->maxLength(50), Forms\Components\DatePicker::make('data_emissao') ->label('Data Emissão') ->default(now()) ->required(), Forms\Components\DatePicker::make('data_vencimento') ->label('Vencimento') ->required(), Forms\Components\Select::make('conta_bancaria_id') ->label('Banco (conta a pagar)') ->options(fn () => \App\Models\ContaBancaria::where('ativo', true)->orderBy('nome')->pluck('nome', 'id')->toArray()) ->searchable() ->placeholder('Selecione o banco'), ]) ->modalHeading('Fechar Fatura de Transportadora') ->modalSubmitActionLabel('Fechar Fatura') ->action(function (array $data) { $ctes = Cte::whereIn('id', $data['ctes_selecionados'])->get(); $valorTotal = (float) $ctes->sum('valor_frete'); $fatura = FaturaTransportadora::create([ 'id_transportadora' => $data['id_transportadora'], 'numero_fatura' => $data['numero_fatura'], 'data_emissao' => $data['data_emissao'], 'valor_total' => round($valorTotal, 2), 'data_vencimento' => $data['data_vencimento'], ]); Cte::whereIn('id', $data['ctes_selecionados'])->update(['id_fatura' => $fatura->id_fatura]); $transportadora = Transportadora::find($data['id_transportadora']); ContaPagar::create([ 'id_fatura' => $fatura->id_fatura, 'descricao' => "Fatura {$data['numero_fatura']} — {$transportadora->nome_transportadora}", 'valor_parcela' => round($valorTotal, 2), 'data_vencimento' => $data['data_vencimento'], 'data_lancamento' => $data['data_emissao'], 'status' => 'pendente', 'numero_parcela' => 1, 'total_parcelas' => 1, 'forma_pagamento' => 'Boleto', 'lancamento_manual' => true, 'conta_bancaria_id' => $data['conta_bancaria_id'] ?? null, ]); Notification::make() ->title("Fatura #{$data['numero_fatura']} criada — " . $ctes->count() . " CTe(s) — R$ " . number_format($valorTotal, 2, ',', '.')) ->success() ->send(); }), ])