From ba211d21d8d8cd95c1125b55cf39b93cb0418a79 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:15:11 -0500 Subject: [PATCH] up --- .../Pages/EditTintoreria.php | 14 +-- .../AjustesRelationManager.php | 42 +++++--- .../CobrosRelationManager.php | 38 ++++--- app/Models/Tintoreria.php | 101 ++++++++++-------- 4 files changed, 118 insertions(+), 77 deletions(-) diff --git a/app/Filament/Resources/TintoreriaResource/Pages/EditTintoreria.php b/app/Filament/Resources/TintoreriaResource/Pages/EditTintoreria.php index 15725ca..858d66d 100644 --- a/app/Filament/Resources/TintoreriaResource/Pages/EditTintoreria.php +++ b/app/Filament/Resources/TintoreriaResource/Pages/EditTintoreria.php @@ -68,7 +68,7 @@ class EditTintoreria extends EditRecord $this->redirect(route('filament.admin.resources.tintorerias.edit', ['record' => $record->id])); }), - // Reprocesos: registrar reproceso como ajuste + // Reprocesos: registrar reproceso como ajuste (igual que arreglos en confecciones) Actions\Action::make('reprocesos') ->label('Reprocesos') ->modalHeading('Registrar Reproceso') @@ -78,8 +78,7 @@ class EditTintoreria extends EditRecord ->numeric() ->required() ->minValue(1) - ->maxValue(fn () => $this->getRecord()->recibido_total ?? 0) - ->helperText(fn () => 'Máximo: ' . ($this->getRecord()->recibido_total ?? 0)), + ->helperText('Prendas que llegaron defectuosas y necesitan reparación'), Forms\Components\Textarea::make('notas'), ]) ->action(function (array $data): void { @@ -90,7 +89,7 @@ class EditTintoreria extends EditRecord try { $record->registerReproceso($cantidad, $data['notas'] ?? null, auth()->id() ?? null); - \Filament\Notifications\Notification::make()->success()->title('Reproceso registrado')->body('Se registró el reproceso correctamente.')->send(); + \Filament\Notifications\Notification::make()->success()->title('Reproceso registrado')->body('Se registró el reproceso correctamente. Estas prendas podrán re-recibirse cuando regresen reparadas.')->send(); } catch (\Illuminate\Validation\ValidationException $e) { \Filament\Notifications\Notification::make()->danger()->title('Error')->body($e->validator->errors()->first() ?? 'Error al registrar reproceso.')->send(); } catch (\Throwable $e) { @@ -100,7 +99,7 @@ class EditTintoreria extends EditRecord $this->redirect(route('filament.admin.resources.tintorerias.edit', ['record' => $record->id])); }), - // Cobros: descontar inventario y registrar cobro + // Cobros: registrar prendas que no llegaron Actions\Action::make('cobros') ->label('Cobros') ->modalHeading('Registrar Cobro') @@ -109,7 +108,8 @@ class EditTintoreria extends EditRecord ->label('Cantidad') ->numeric() ->required() - ->minValue(1), + ->minValue(1) + ->helperText('Prendas que NO llegaron y se cobran a la tintorería'), Forms\Components\TextInput::make('valor') ->label('Valor a descontar') ->numeric() @@ -126,7 +126,7 @@ class EditTintoreria extends EditRecord try { $record->registerCobro($cantidad, $valor, $data['notas'] ?? null, auth()->id() ?? null); - \Filament\Notifications\Notification::make()->success()->title('Cobro registrado')->body('Se descontó del inventario y se registró el cobro.')->send(); + \Filament\Notifications\Notification::make()->success()->title('Cobro registrado')->body('Se registró el cobro correctamente.')->send(); } catch (\Illuminate\Validation\ValidationException $e) { \Filament\Notifications\Notification::make()->danger()->title('Error')->body($e->validator->errors()->first() ?? 'Error al registrar cobro.')->send(); } catch (\Throwable $e) { diff --git a/app/Filament/Resources/TintoreriaResource/RelationManagers/AjustesRelationManager.php b/app/Filament/Resources/TintoreriaResource/RelationManagers/AjustesRelationManager.php index 3bf86a8..14ddedc 100644 --- a/app/Filament/Resources/TintoreriaResource/RelationManagers/AjustesRelationManager.php +++ b/app/Filament/Resources/TintoreriaResource/RelationManagers/AjustesRelationManager.php @@ -10,6 +10,7 @@ class AjustesRelationManager extends RelationManager { protected static string $relationship = 'ajustes'; protected static ?string $recordTitleAttribute = 'id'; + protected static ?string $title = 'Reprocesos'; public function table(Tables\Table $table): Tables\Table { @@ -22,20 +23,35 @@ class AjustesRelationManager extends RelationManager TextColumn::make('notas')->limit(80)->wrap(), TextColumn::make('created_at')->label('Fecha')->dateTime(), ]) - ->filters([]) + ->filters([ + Tables\Filters\SelectFilter::make('tipo') + ->options([ + 'reproceso' => 'Reproceso', + ]) + ->default('reproceso') + ]) + ->modifyQueryUsing(fn ($query) => $query->where('tipo', 'reproceso')) ->headerActions([ - Tables\Actions\CreateAction::make()->form([ - \Filament\Forms\Components\TextInput::make('cantidad')->numeric()->required()->minValue(1), - \Filament\Forms\Components\Textarea::make('notas'), - ])->action(function (array $data) { - $owner = $this->getOwnerRecord(); - try { - $owner->registerReproceso((int)$data['cantidad'], $data['notas'] ?? null, auth()->id() ?? null); - \Filament\Notifications\Notification::make()->success()->title('Reproceso registrado')->send(); - } catch (\Throwable $e) { - \Filament\Notifications\Notification::make()->danger()->title('Error')->body($e->getMessage())->send(); - } - }), + Tables\Actions\CreateAction::make() + ->label('Agregar reproceso') + ->form([ + \Filament\Forms\Components\TextInput::make('cantidad') + ->numeric() + ->required() + ->minValue(1) + ->helperText('Prendas que llegaron defectuosas y necesitan reparación'), + \Filament\Forms\Components\Textarea::make('notas'), + ])->action(function (array $data) { + $owner = $this->getOwnerRecord(); + try { + $owner->registerReproceso((int)$data['cantidad'], $data['notas'] ?? null, auth()->id() ?? null); + \Filament\Notifications\Notification::make()->success()->title('Reproceso registrado')->body('Estas prendas podrán re-recibirse cuando regresen reparadas.')->send(); + } catch (\Illuminate\Validation\ValidationException $e) { + \Filament\Notifications\Notification::make()->danger()->title('Error')->body($e->validator->errors()->first())->send(); + } catch (\Throwable $e) { + \Filament\Notifications\Notification::make()->danger()->title('Error')->body($e->getMessage())->send(); + } + }), ]) ->actions([]) ->bulkActions([]); diff --git a/app/Filament/Resources/TintoreriaResource/RelationManagers/CobrosRelationManager.php b/app/Filament/Resources/TintoreriaResource/RelationManagers/CobrosRelationManager.php index 8686bfc..0d06b61 100644 --- a/app/Filament/Resources/TintoreriaResource/RelationManagers/CobrosRelationManager.php +++ b/app/Filament/Resources/TintoreriaResource/RelationManagers/CobrosRelationManager.php @@ -24,19 +24,31 @@ class CobrosRelationManager extends RelationManager ]) ->filters([]) ->headerActions([ - Tables\Actions\CreateAction::make()->form([ - \Filament\Forms\Components\TextInput::make('cantidad')->numeric()->required()->minValue(1), - \Filament\Forms\Components\TextInput::make('valor')->numeric()->required()->minValue(0), - \Filament\Forms\Components\Textarea::make('notas'), - ])->action(function (array $data) { - $owner = $this->getOwnerRecord(); - try { - $owner->registerCobro((int)$data['cantidad'], (float)$data['valor'], $data['notas'] ?? null, auth()->id() ?? null); - \Filament\Notifications\Notification::make()->success()->title('Cobro registrado')->send(); - } catch (\Throwable $e) { - \Filament\Notifications\Notification::make()->danger()->title('Error')->body($e->getMessage())->send(); - } - }), + Tables\Actions\CreateAction::make() + ->label('Agregar cobro') + ->form([ + \Filament\Forms\Components\TextInput::make('cantidad') + ->numeric() + ->required() + ->minValue(1) + ->helperText('Prendas que NO llegaron y se cobran a la tintorería'), + \Filament\Forms\Components\TextInput::make('valor') + ->numeric() + ->required() + ->minValue(0) + ->label('Valor a descontar'), + \Filament\Forms\Components\Textarea::make('notas'), + ])->action(function (array $data) { + $owner = $this->getOwnerRecord(); + try { + $owner->registerCobro((int)$data['cantidad'], (float)$data['valor'], $data['notas'] ?? null, auth()->id() ?? null); + \Filament\Notifications\Notification::make()->success()->title('Cobro registrado')->send(); + } catch (\Illuminate\Validation\ValidationException $e) { + \Filament\Notifications\Notification::make()->danger()->title('Error')->body($e->validator->errors()->first())->send(); + } catch (\Throwable $e) { + \Filament\Notifications\Notification::make()->danger()->title('Error')->body($e->getMessage())->send(); + } + }), ]) ->actions([]) ->bulkActions([]); diff --git a/app/Models/Tintoreria.php b/app/Models/Tintoreria.php index 77c5a8e..e6a9428 100644 --- a/app/Models/Tintoreria.php +++ b/app/Models/Tintoreria.php @@ -64,7 +64,8 @@ class Tintoreria extends Model } /** - * Registrar un reproceso: crear ajuste histórico y traslado de reproceso + * Registrar un reproceso: prendas que llegaron mal y salen a reparación + * Permiten re-recepción cuando regresen arregladas (igual que arreglos en confecciones) */ public function registerReproceso(int $cantidad, ?string $notas = null, ?int $userId = null) { @@ -72,9 +73,25 @@ class Tintoreria extends Model throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => 'La cantidad debe ser mayor que 0.']); } - $recibido = $this->recibido_total; - if ($cantidad > $recibido) { - throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => 'La cantidad supera lo recibido.']); + // Validar que no exceda las recepciones efectivas (solo puedes marcar como reproceso lo que YA recibiste) + $recepcionesEfectivas = (int) $this->recepciones()->sum(\DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)')); + $reprocesosActuales = (int) $this->ajustes()->where('tipo', 'reproceso')->sum('cantidad'); + + $disponibleParaReprocesos = $recepcionesEfectivas - $reprocesosActuales; + + if ($cantidad > $disponibleParaReprocesos) { + throw \Illuminate\Validation\ValidationException::withMessages([ + 'cantidad' => "No puedes registrar {$cantidad} prendas como reproceso. + + 📊 Resumen: + • Recepciones efectivas: {$recepcionesEfectivas} + • Reprocesos ya registrados: {$reprocesosActuales} + • Disponible para marcar como reproceso: {$disponibleParaReprocesos} + + ➡️ Solo puedes registrar máximo {$disponibleParaReprocesos} prendas como reproceso. + + 💡 Los reprocesos deben ser de prendas YA recibidas que necesitan reparación." + ]); } // Crear ajuste de tipo reproceso @@ -87,32 +104,12 @@ class Tintoreria extends Model 'notas' => $notas, ]); - // Crear traslado de reprocesos para reflejar la operación - \App\Models\TrasladoPrenda::create([ - 'orden_produccion_id' => $this->orden_produccion_id, - 'referencia_type' => self::class, - 'referencia_id' => $this->id, - 'origen' => 'tintoreria', - 'destino' => 'reprocesos', - 'cantidad_enviada' => $cantidad, - 'cantidad_recibida' => 0, - 'prendas_defectuosas' => 0, - 'reparaciones' => 0, - 'saldos' => 0, - 'fecha_envio' => now(), - 'fecha_recepcion' => now(), - 'estado' => 'recibido', - ]); - - // Recalcular cantidad_recibida almacenada para compatibilidad con UI - $this->cantidad_recibida = $this->recibido_total; - $this->save(); - return $aj; } /** - * Registrar un cobro: descontar inventario y crear registro de cobro + * Registrar un cobro: solo registra el cobro sin descontar inventario + * Los cobros son prendas que NO llegaron - se cobran a la tintorería */ public function registerCobro(int $cantidad, float $valor, ?string $notas = null, ?int $userId = null) { @@ -120,25 +117,36 @@ class Tintoreria extends Model throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => 'La cantidad debe ser mayor que 0.']); } - // Buscar inventario disponible - $inventario = \App\Models\InventarioPrenda::where('orden_produccion_id', $this->orden_produccion_id) - ->where('cantidad_disponible', '>=', $cantidad) - ->first(); - - if (! $inventario) { - throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => 'No hay inventario disponible suficiente para descontar.']); + // Validar que no se exceda lo enviado + $enviada = (int) ($this->cantidad_enviada ?? 0); + if ($enviada <= 0) { + throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => 'La tintorería no tiene cantidad_enviada registrada.']); } - // Descontar del inventario - $inventario->cantidad_disponible = $inventario->cantidad_disponible - $cantidad; - $inventario->save(); + // Calcular lo que ya se ha contabilizado + $recepcionesEfectivas = (int) $this->recepciones()->sum(\DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)')); + $reprocesos = (int) $this->ajustes()->where('tipo', 'reproceso')->sum('cantidad'); + $recibidoNeto = $recepcionesEfectivas - $reprocesos; // Los reprocesos permiten re-recepción + + $cobrosActuales = (int) $this->cobros()->sum('cantidad'); + + $totalContabilizado = $recibidoNeto + $cobrosActuales; + $disponible = $enviada - $totalContabilizado; - // Si está asociado a producto, decrementar stock - if ($inventario->producto_id) { - $producto = \App\Models\Producto::find($inventario->producto_id); - if ($producto) { - $producto->decrement('stock', $cantidad); - } + if ($cantidad > $disponible) { + throw \Illuminate\Validation\ValidationException::withMessages([ + 'cantidad' => "No puedes registrar {$cantidad} prendas como cobro. + + 📊 Resumen: + • Enviadas: {$enviada} + • Recepciones efectivas: {$recepcionesEfectivas} + • Reprocesos (permiten re-recepción): {$reprocesos} + • Recibido neto: {$recibidoNeto} + • Cobros registrados: {$cobrosActuales} + • Disponible para cobros: {$disponible} + + ➡️ Solo puedes registrar máximo {$disponible} prendas más como cobros." + ]); } // Crear registro de cobro @@ -194,10 +202,15 @@ class Tintoreria extends Model return (float) $this->cobros()->sum('valor'); } - // Cuantas faltan por recibir (usa la suma real de recepciones) + // Cuantas faltan por recibir (considera cobros porque ya están contabilizados) public function getFaltantesAttribute() { - return max(0, (int)($this->cantidad_enviada ?? 0) - $this->recibido_total - $this->perdidas_total); + $enviada = (int)($this->cantidad_enviada ?? 0); + $recibidoNeto = $this->recibido_total; + $cobros = (int) $this->cobros()->sum('cantidad'); + + // Lo que puede recibir = enviadas - cobros - recibido_neto + return max(0, $enviada - $cobros - $recibidoNeto); } // Estado de recepción (Parcial / Completado)