schema([ Section::make('Información del Proceso') ->schema([ Select::make('orden_produccion_id') ->label('Orden de Producción') ->options(fn () => \App\Models\OrdenProduccion::all()->pluck('numero_orden', 'id')->toArray()) ->searchable() ->reactive() ->required() ->afterStateUpdated(function ($state, $set) { if ($state) { // Calcular cantidad disponible desde Tintorería (neto: recepciones - reprocesos - cobros) $tintoreriaRecords = \App\Models\Tintoreria::where('orden_produccion_id', $state)->get(); $disponible = 0; foreach ($tintoreriaRecords as $tintoreria) { $recepciones = (int) $tintoreria->recepciones()->sum(DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)')); $reprocesos = (int) $tintoreria->ajustes()->where('tipo', 'reproceso')->sum('cantidad'); $disponible += ($recepciones - $reprocesos); } // Restar lo que ya se envió a otros acabados $yaEnAcabados = \App\Models\ProcesoAcabado::where('orden_produccion_id', $state) ->sum('cantidad_enviada'); $disponible = max(0, $disponible - $yaEnAcabados); $set('cantidad_enviada', $disponible); $orden = \App\Models\OrdenProduccion::find($state); $set('prenda_nombre_info', $orden->prenda_modelo ?? 'Sin especificar'); } else { $set('prenda_nombre_info', null); } }), TextInput::make('prenda_nombre_info') ->label('Prenda') ->disabled() ->dehydrated(false) ->visible(fn ($get) => $get('orden_produccion_id') !== null) ->helperText('Nombre de la prenda de la orden de producción seleccionada'), TextInput::make('tipo_proceso') ->label('Tipo de proceso') ->placeholder('Ej: Planchado, Empacado, etc.') ->required(), TextInput::make('cantidad_enviada') ->label('Cantidad a enviar') ->numeric() ->required() ->minValue(1) ->reactive() ->helperText(function ($get) { $op_id = $get('orden_produccion_id'); if (!$op_id) return 'Selecciona una orden de producción primero'; // Calcular disponible neto desde Tintorería $tintoreriaRecords = \App\Models\Tintoreria::where('orden_produccion_id', $op_id)->get(); $disponible = 0; foreach ($tintoreriaRecords as $tintoreria) { $recepciones = (int) $tintoreria->recepciones()->sum(DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)')); $reprocesos = (int) $tintoreria->ajustes()->where('tipo', 'reproceso')->sum('cantidad'); $disponible += ($recepciones - $reprocesos); } // Restar lo que ya se envió a otros acabados $yaEnAcabados = \App\Models\ProcesoAcabado::where('orden_produccion_id', $op_id) ->sum('cantidad_enviada'); $disponible = max(0, $disponible - $yaEnAcabados); return 'Disponible desde Tintorería: ' . $disponible; }), DatePicker::make('fecha_envio') ->label('Fecha de envío') ->required() ->default(now()), ]) ->columns(2), Section::make('Distribución de Proveedores') ->description('Asigna la cantidad a enviar a cada proveedor') ->schema([ Forms\Components\Repeater::make('distribucion_proveedores') ->label('') ->schema([ Select::make('proveedor_id') ->label('Proveedor') ->options(\App\Models\Proveedor::where('categoria', 'empleado')->pluck('nombre', 'id')) ->searchable() ->required(), TextInput::make('cantidad') ->label('Cantidad') ->numeric() ->required() ->minValue(1) ->live() ->afterStateUpdated(function ($state, $set, $get) { // Validar que la suma no exceda el total disponible $total = (int) ($get('../../cantidad_enviada') ?? 0); $distribuciones = $get('../../distribucion_proveedores') ?? []; $suma = 0; foreach ($distribuciones as $dist) { $suma += (int) ($dist['cantidad'] ?? 0); } if ($suma > $total) { throw \Illuminate\Validation\ValidationException::withMessages([ 'cantidad' => "La suma de las distribuciones ({$suma}) excede el total disponible ({$total})." ]); } }), ]) ->columns(2) ->defaultItems(1) ->addActionLabel('Agregar otro proveedor') ->collapsible() ->cloneable() ->live() ->helperText(function ($get) { $total = (int) ($get('cantidad_enviada') ?? 0); $distribuciones = $get('distribucion_proveedores') ?? []; $suma = 0; foreach ($distribuciones as $dist) { $suma += (int) ($dist['cantidad'] ?? 0); } $restante = $total - $suma; if ($restante < 0) { return "⚠️ Excede el total por " . abs($restante) . " unidades"; } elseif ($restante > 0) { return "Faltan {$restante} unidades por distribuir"; } else { return "✓ Total distribuido correctamente"; } }), ]) ->collapsed(fn ($get) => !$get('distribucion_proveedores')), Section::make('Estado y Resumen') ->schema([ Forms\Components\Placeholder::make('resumen_estado') ->label('Resumen del Proceso') ->content(function ($get, $record) { if (!$record) { return 'Guarda el registro para ver el resumen'; } $enviadas = (int) ($record->cantidad_enviada ?? 0); $recibidas = $record->recibido_total ?? 0; $defectuosas = $record->perdidas_total ?? 0; $faltantes = $record->faltantes ?? 0; $estado = $record->estado_recepcion ?? 'Sin envío'; $html = '
'; $html .= '
📊 Resumen de Estado
'; $html .= '
'; // Estado general $colorEstado = match($estado) { 'Completado' => '#10b981', 'Parcial' => '#f59e0b', default => '#6b7280' }; $html .= '
Estado: ' . $estado . '
'; // Enviadas $html .= '
Enviadas: ' . $enviadas . '
'; // Recibidas $html .= '
Recibidas: ' . $recibidas . '
'; // Pendientes/Faltantes $colorFaltantes = $faltantes > 0 ? '#f59e0b' : '#10b981'; $html .= '
Pendientes: ' . $faltantes . '
'; // Defectuosas if ($defectuosas > 0) { $html .= '
Defectuosas: ' . $defectuosas . '
'; } // Entregadas (listas para inventario) $entregadas = max(0, $recibidas); $html .= '
Listas p/Inventario: ' . $entregadas . '
'; $html .= '
'; return new \Illuminate\Support\HtmlString($html); }) ->visible(fn ($record) => $record !== null), Forms\Components\Checkbox::make('terminada') ->label('Marcar como terminada') ->helperText('Marca esta casilla cuando el proceso haya finalizado'), Textarea::make('observaciones') ->label('Observaciones') ->rows(3) ->nullable(), ]) ->columns(1), ]); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('ordenProduccion.numero_orden') ->label('OP') ->searchable() ->sortable(), TextColumn::make('ordenProduccion.referencia') ->label('Referencia') ->searchable() ->toggleable(), TextColumn::make('ordenProduccion.prenda_modelo') ->label('Prenda') ->searchable() ->limit(30) ->toggleable(), TextColumn::make('tipo_proceso') ->label('Tipo de proceso') ->searchable(), TextColumn::make('cantidad_enviada') ->label('Cantidad') ->sortable(), TextColumn::make('cantidad_recibida_total') ->label('Recibidas') ->getStateUsing(fn ($record) => $record->recibido_total ?? 0) ->badge() ->color('success'), TextColumn::make('estado_recepcion') ->label('Estado Recepción') ->getStateUsing(fn ($record) => $record->estado_recepcion ?? 'Sin envío') ->badge() ->color(fn ($state) => match($state) { 'Completado' => 'success', 'Parcial' => 'warning', default => 'gray', }), TextColumn::make('proveedores_asignados') ->label('Proveedores') ->getStateUsing(function ($record) { if (!$record->distribucion_proveedores) return 'Sin asignar'; $distribuciones = collect($record->distribucion_proveedores); return $distribuciones->map(function ($dist) { $proveedor = \App\Models\Proveedor::find($dist['proveedor_id'] ?? null); $nombre = $proveedor ? $proveedor->nombre : 'Desconocido'; return "{$nombre} ({$dist['cantidad']})"; })->join(', '); }) ->wrap() ->limit(50), BadgeColumn::make('terminada') ->label('Estado') ->formatStateUsing(fn($state) => $state ? 'Terminada' : 'En proceso') ->colors([ 'warning' => false, 'success' => true, ]), TextColumn::make('fecha_envio') ->label('Fecha envío') ->date() ->sortable(), TextColumn::make('created_at') ->label('Creado') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), ]) ->defaultSort('created_at', 'desc') ->filters([ Tables\Filters\SelectFilter::make('orden_produccion_id') ->label('Orden de Producción') ->relationship('ordenProduccion', 'numero_orden') ->searchable() ->preload(), Tables\Filters\TernaryFilter::make('terminada') ->label('Estado') ->placeholder('Todos') ->trueLabel('Terminadas') ->falseLabel('En proceso'), ]) ->actions([ Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]); } public static function getRelations(): array { return [ RelationManagers\RecepcionesRelationManager::class, RelationManagers\AjustesRelationManager::class, ]; } public static function getPages(): array { return [ 'index' => Pages\ListProcesoAcabados::route('/'), 'create' => Pages\CreateProcesoAcabado::route('/create'), 'edit' => Pages\EditProcesoAcabado::route('/{record}/edit'), ]; } }