From 02490b3d39089ec2df57497631d87bbf962ddadd Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:39:21 -0500 Subject: [PATCH] up --- .../Resources/OjalPresillaResource.php | 210 ++++++++++-------- .../Pages/CreateOjalPresilla.php | 45 +++- app/Models/Ojal.php | 18 ++ app/Models/Prensilla.php | 18 ++ ...nada_and_tipo_to_ojales_and_prensillas.php | 38 ++++ 5 files changed, 225 insertions(+), 104 deletions(-) create mode 100644 database/migrations/2026_01_30_013611_add_terminada_and_tipo_to_ojales_and_prensillas.php diff --git a/app/Filament/Resources/OjalPresillaResource.php b/app/Filament/Resources/OjalPresillaResource.php index 8e0c980..030c0ba 100644 --- a/app/Filament/Resources/OjalPresillaResource.php +++ b/app/Filament/Resources/OjalPresillaResource.php @@ -4,13 +4,13 @@ namespace App\Filament\Resources; use App\Filament\Resources\OjalPresillaResource\Pages; use App\Models\Ojal; -use App\Models\Prensilla; use Filament\Forms; use Filament\Resources\Resource; use Filament\Tables; use Filament\Forms\Components\Select; use Filament\Forms\Components\TextInput; -use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Repeater; +use Filament\Forms\Components\Checkbox; use Filament\Tables\Filters\SelectFilter; class OjalPresillaResource extends Resource @@ -29,124 +29,142 @@ class OjalPresillaResource extends Resource public static function form(Forms\Form $form): Forms\Form { return $form->schema([ - Select::make('tipo') - ->label('Tipo') - ->options([ - 'ojal' => 'Ojal', - 'prensilla' => 'Prensilla', - ]) - ->required() - ->live() - ->afterStateUpdated(fn ($set) => $set('proveedor_id', null)), - - Select::make('proveedor_id') - ->label('Proveedor') - ->options(function ($get) { - $tipo = $get('tipo') ?? 'prensilla_ojal'; - return \App\Models\Proveedor::where('categoria', 'prensilla_ojal')->pluck('nombre', 'id'); - }) - ->searchable() - ->preload() - ->nullable(), - Select::make('orden_produccion_id') ->label('Orden de Producción') - ->relationship('ordenProduccion', 'numero_orden') + ->options(function () { + // Solo OPs que tienen confecciones recibidas + return \App\Models\OrdenProduccion::whereHas('confecciones', function ($q) { + $q->where('cantidad_recibida', '>', 0); + })->get()->mapWithKeys(function ($op) { + $recibido = $op->confecciones->sum('cantidad_recibida'); + return [$op->id => "{$op->numero_orden} ({$recibido} prendas recibidas)"]; + }); + }) ->searchable() - ->preload() - ->required(), - - DatePicker::make('fecha_envio') - ->label('Fecha de envío') - ->default(now()) - ->required(), - - TextInput::make('cantidad_enviada') - ->label('Cantidad enviada') - ->numeric() ->required() - ->minValue(1), + ->live() + ->afterStateUpdated(function ($state, $set) { + if ($state) { + $op = \App\Models\OrdenProduccion::find($state); + if ($op) { + $recibido = $op->confecciones->sum('cantidad_recibida'); + $set('cantidad_enviada', $recibido); + } + } + }), - DatePicker::make('fecha_recepcion') - ->label('Fecha de recepción'), - - TextInput::make('cantidad_recibida') - ->label('Cantidad recibida') + TextInput::make('tipo') + ->label('Tipo') + ->placeholder('Ej: Ojal, Prensilla, Botón, etc.') + ->required() + ->helperText('Especifica el tipo de accesorio'), + + TextInput::make('cantidad_enviada') + ->label('Cantidad total') ->numeric() - ->nullable(), - - TextInput::make('perdidas') - ->label('Pérdidas') - ->numeric() - ->default(0), + ->readOnly() + ->helperText('Se calcula automáticamente desde confecciones recibidas'), + + Repeater::make('distribuciones') + ->label('Distribución entre proveedores') + ->schema([ + Select::make('proveedor_id') + ->label('Proveedor') + ->options(fn () => \App\Models\Proveedor::where('categoria', 'prensilla_ojal')->pluck('nombre', 'id')) + ->searchable() + ->required(), + + TextInput::make('cantidad') + ->label('Cantidad') + ->numeric() + ->required() + ->minValue(1), + ]) + ->defaultItems(1) + ->addActionLabel('Agregar proveedor') + ->helperText('Puedes distribuir la cantidad entre varios proveedores'), + + Checkbox::make('terminada') + ->label('Marcar como terminada') + ->helperText('Al marcar terminada, se enviará automáticamente a tintorería') + ->live(), ]); } public static function table(Tables\Table $table): Tables\Table { return $table - ->modifyQueryUsing(function ($query) { - // Union de ambas tablas - $ojales = \App\Models\Ojal::select([ - 'id', - 'proveedor_id', - 'orden_produccion_id', - 'fecha_envio', - 'cantidad_enviada', - 'fecha_recepcion', - 'cantidad_recibida', - 'perdidas', - 'created_at', - \DB::raw("'ojal' as tipo") - ]); - - $prensillas = \App\Models\Prensilla::select([ - 'id', - 'proveedor_id', - 'orden_produccion_id', - 'fecha_envio', - 'cantidad_enviada', - 'fecha_recepcion', - 'cantidad_recibida', - 'perdidas', - 'created_at', - \DB::raw("'prensilla' as tipo") - ]); - - return $query->from(\DB::raw("({$ojales->toSql()} UNION ALL {$prensillas->toSql()}) as combined")) - ->mergeBindings($ojales->getQuery()) - ->mergeBindings($prensillas->getQuery()); - }) ->columns([ Tables\Columns\TextColumn::make('tipo') + ->label('Tipo') + ->searchable() + ->sortable() ->badge() - ->color(fn (string $state): string => match ($state) { - 'ojal' => 'info', - 'prensilla' => 'success', - }) - ->formatStateUsing(fn (string $state): string => ucfirst($state)), + ->color('info'), - Tables\Columns\TextColumn::make('proveedor_id') - ->label('Proveedor') - ->formatStateUsing(fn ($state) => \App\Models\Proveedor::find($state)?->nombre ?? 'N/A'), - - Tables\Columns\TextColumn::make('orden_produccion_id') + Tables\Columns\TextColumn::make('ordenProduccion.numero_orden') ->label('OP') - ->formatStateUsing(fn ($state) => \App\Models\OrdenProduccion::find($state)?->numero_orden ?? 'N/A'), + ->searchable() + ->sortable(), - Tables\Columns\TextColumn::make('cantidad_enviada')->label('Enviadas'), - Tables\Columns\TextColumn::make('cantidad_recibida')->label('Recibidas'), - Tables\Columns\TextColumn::make('perdidas')->label('Pérdidas'), - Tables\Columns\TextColumn::make('fecha_envio')->date()->label('F. Envío'), - Tables\Columns\TextColumn::make('fecha_recepcion')->date()->label('F. Recepción'), + Tables\Columns\TextColumn::make('cantidad_enviada') + ->label('Cantidad') + ->sortable(), + + Tables\Columns\TextColumn::make('proveedor.nombre') + ->label('Proveedor') + ->searchable() + ->default('Sin asignar'), + + Tables\Columns\IconColumn::make('terminada') + ->boolean() + ->label('Terminada') + ->sortable(), + + Tables\Columns\TextColumn::make('fecha_envio') + ->date() + ->label('Fecha envío') + ->sortable(), + + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->label('Creado') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ SelectFilter::make('tipo') + ->label('Tipo'), + + SelectFilter::make('terminada') ->options([ - 'ojal' => 'Ojal', - 'prensilla' => 'Prensilla', + '0' => 'Pendiente', + '1' => 'Terminada', ]) ]) + ->actions([ + Tables\Actions\EditAction::make(), + Tables\Actions\Action::make('marcar_terminada') + ->label('Terminar') + ->icon('heroicon-o-check-circle') + ->color('success') + ->visible(fn ($record) => !$record->terminada) + ->action(function ($record) { + $record->terminada = true; + $record->save(); + + \Filament\Notifications\Notification::make() + ->success() + ->title('Marcado como terminado') + ->body('Se ha creado automáticamente el envío a tintorería') + ->send(); + }), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]) ->defaultSort('created_at', 'desc'); } diff --git a/app/Filament/Resources/OjalPresillaResource/Pages/CreateOjalPresilla.php b/app/Filament/Resources/OjalPresillaResource/Pages/CreateOjalPresilla.php index b71ae48..0e98287 100644 --- a/app/Filament/Resources/OjalPresillaResource/Pages/CreateOjalPresilla.php +++ b/app/Filament/Resources/OjalPresillaResource/Pages/CreateOjalPresilla.php @@ -11,19 +11,48 @@ class CreateOjalPresilla extends CreateRecord protected function mutateFormDataBeforeCreate(array $data): array { - $tipo = $data['tipo'] ?? 'ojal'; - unset($data['tipo']); + $distribuciones = $data['distribuciones'] ?? []; + $tipo = $data['tipo'] ?? 'accesorio'; + $ordenProduccionId = $data['orden_produccion_id']; + $cantidadTotal = $data['cantidad_enviada']; + $terminada = $data['terminada'] ?? false; - // Guardar en la tabla correspondiente - if ($tipo === 'prensilla') { - $record = \App\Models\Prensilla::create($data); - } else { - $record = \App\Models\Ojal::create($data); + // Validar que las distribuciones sumen la cantidad total + $sumaDistribucion = array_sum(array_column($distribuciones, 'cantidad')); + if ($sumaDistribucion != $cantidadTotal) { + throw \Illuminate\Validation\ValidationException::withMessages([ + 'distribuciones' => "La suma de las distribuciones ({$sumaDistribucion}) debe ser igual a la cantidad total ({$cantidadTotal})." + ]); + } + + // Crear un registro por cada proveedor + foreach ($distribuciones as $distribucion) { + \App\Models\Ojal::create([ + 'proveedor_id' => $distribucion['proveedor_id'], + 'orden_produccion_id' => $ordenProduccionId, + 'tipo' => $tipo, + 'fecha_envio' => now(), + 'cantidad_enviada' => $distribucion['cantidad'], + 'terminada' => $terminada, + ]); } // Redirigir al listado $this->redirect(OjalPresillaResource::getUrl('index')); - return $data; + // Retornar vacío para evitar que Filament intente crear el registro automáticamente + return []; + } + + protected function afterCreate(): void + { + // Ya se crearon los registros en mutateFormDataBeforeCreate + // Redirigir al index + $this->redirect(OjalPresillaResource::getUrl('index')); + } + + protected function getRedirectUrl(): string + { + return OjalPresillaResource::getUrl('index'); } } diff --git a/app/Models/Ojal.php b/app/Models/Ojal.php index d77f3ce..af4f34c 100644 --- a/app/Models/Ojal.php +++ b/app/Models/Ojal.php @@ -11,11 +11,17 @@ class Ojal extends Model protected $fillable = [ 'proveedor_id', 'orden_produccion_id', + 'tipo', 'fecha_envio', 'cantidad_enviada', 'fecha_recepcion', 'cantidad_recibida', 'perdidas', + 'terminada', + ]; + + protected $casts = [ + 'terminada' => 'boolean', ]; public function proveedor() @@ -41,5 +47,17 @@ class Ojal extends Model throw \Illuminate\Validation\ValidationException::withMessages(['cantidad_enviada' => 'No hay suficientes unidades disponibles desde confecciones para asignar.']); } }); + + static::saved(function ($registro) { + // Si se marca como terminada, crear traslado automático a tintororía + if ($registro->terminada && !$registro->getOriginal('terminada')) { + \App\Models\Tintoreria::create([ + 'orden_produccion_id' => $registro->orden_produccion_id, + 'fecha_envio' => now(), + 'cantidad_enviada' => $registro->cantidad_enviada, + 'observaciones' => "Auto-enviado desde {$registro->tipo} (ID: {$registro->id})", + ]); + } + }); } } diff --git a/app/Models/Prensilla.php b/app/Models/Prensilla.php index 464dac2..f4ba52f 100644 --- a/app/Models/Prensilla.php +++ b/app/Models/Prensilla.php @@ -9,11 +9,17 @@ class Prensilla extends Model protected $fillable = [ 'proveedor_id', 'orden_produccion_id', + 'tipo', 'fecha_envio', 'cantidad_enviada', 'fecha_recepcion', 'cantidad_recibida', 'perdidas', + 'terminada', + ]; + + protected $casts = [ + 'terminada' => 'boolean', ]; public function proveedor() @@ -39,5 +45,17 @@ class Prensilla extends Model throw \Illuminate\Validation\ValidationException::withMessages(['cantidad_enviada' => 'No hay suficientes unidades disponibles desde confecciones para asignar.']); } }); + + static::saved(function ($registro) { + // Si se marca como terminada, crear traslado automático a tintororía + if ($registro->terminada && !$registro->getOriginal('terminada')) { + \App\Models\Tintoreria::create([ + 'orden_produccion_id' => $registro->orden_produccion_id, + 'fecha_envio' => now(), + 'cantidad_enviada' => $registro->cantidad_enviada, + 'observaciones' => "Auto-enviado desde {$registro->tipo} (ID: {$registro->id})", + ]); + } + }); } } diff --git a/database/migrations/2026_01_30_013611_add_terminada_and_tipo_to_ojales_and_prensillas.php b/database/migrations/2026_01_30_013611_add_terminada_and_tipo_to_ojales_and_prensillas.php new file mode 100644 index 0000000..2862e36 --- /dev/null +++ b/database/migrations/2026_01_30_013611_add_terminada_and_tipo_to_ojales_and_prensillas.php @@ -0,0 +1,38 @@ +string('tipo')->nullable()->after('orden_produccion_id'); + $table->boolean('terminada')->default(false)->after('perdidas'); + }); + + Schema::table('prensillas', function (Blueprint $table) { + $table->string('tipo')->nullable()->after('orden_produccion_id'); + $table->boolean('terminada')->default(false)->after('perdidas'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('ojales', function (Blueprint $table) { + $table->dropColumn(['tipo', 'terminada']); + }); + + Schema::table('prensillas', function (Blueprint $table) { + $table->dropColumn(['tipo', 'terminada']); + }); + } +};