This commit is contained in:
Lizandro Guarnizo
2026-01-29 17:14:05 -05:00
parent f9f5455cd5
commit 35f56cbbe0
7 changed files with 299 additions and 82 deletions
@@ -30,12 +30,8 @@ class EditConfeccion extends EditRecord
->helperText(fn () => 'Máximo: ' . ($this->getRecord()->faltantes ?? 0))
->default(fn () => $this->getRecord()->faltantes ?? 0),
Forms\Components\TextInput::make('prendas_defectuosas')
->numeric()
->minValue(0)
->maxValue(fn ($get) => (int) ($get('cantidad') ?? 0))
->helperText('Opcional. Se restará del total recibido.'),
Forms\Components\Textarea::make('notas'),
Forms\Components\Textarea::make('notas')
->helperText('Si hay prendas defectuosas, regístralas después en "Arreglos".'),
])
->action(function (array $data): void {
$record = $this->getRecord();
@@ -46,7 +42,7 @@ class EditConfeccion extends EditRecord
'referencia_id' => $record->id,
'cantidad' => (int) $data['cantidad'],
'fecha_recepcion' => $data['fecha_recepcion'],
'prendas_defectuosas' => isset($data['prendas_defectuosas']) ? (int) $data['prendas_defectuosas'] : 0,
'prendas_defectuosas' => 0,
'notas' => $data['notas'] ?? null,
]);
@@ -30,14 +30,8 @@ class RecepcionesRelationManager extends RelationManager
->maxValue(fn () => $this->getOwnerRecord()?->faltantes ?? 0)
->helperText(fn () => 'Máximo: ' . ($this->getOwnerRecord()?->faltantes ?? 0)),
TextInput::make('prendas_defectuosas')
->label('Faltantes')
->numeric()
->minValue(0)
->maxValue(fn ($get) => (int) ($get('cantidad') ?? 0))
->helperText('Opcional. Se restará del total recibido.'),
Textarea::make('notas'),
Textarea::make('notas')
->helperText('Si hay prendas defectuosas, regístralas en "Arreglos" después de crear la recepción.'),
]);
}
@@ -48,7 +42,6 @@ class RecepcionesRelationManager extends RelationManager
TextColumn::make('id')->label('#'),
TextColumn::make('fecha_recepcion')->dateTime()->label('Fecha'),
TextColumn::make('cantidad')->label('Cantidad'),
TextColumn::make('prendas_defectuosas')->label('Faltantes'),
TextColumn::make('usuario.name')->label('Usuario'),
TextColumn::make('notas')->limit(50)->wrap(),
TextColumn::make('created_at')->dateTime()->label('Creado'),
+46 -58
View File
@@ -118,7 +118,8 @@ class Confeccion extends Model
}
/**
* Registrar un arreglo como ajuste histórico y crear traslado de reparaciones
* Registrar un arreglo: prendas que llegaron mal y salen a reparación
* Permiten re-recepción cuando regresen arregladas
*/
public function registerArreglo(int $cantidad, ?string $notas = null, ?int $userId = null)
{
@@ -126,9 +127,25 @@ class Confeccion 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 arreglo lo que YA recibiste)
$recepcionesEfectivas = (int) $this->recepciones()->sum(\DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
$arreglosActuales = (int) $this->ajustes()->where('tipo', 'arreglo')->sum('cantidad');
$disponibleParaArreglos = $recepcionesEfectivas - $arreglosActuales;
if ($cantidad > $disponibleParaArreglos) {
throw \Illuminate\Validation\ValidationException::withMessages([
'cantidad' => "No puedes registrar {$cantidad} prendas como arreglo.
📊 Resumen:
Recepciones efectivas: {$recepcionesEfectivas}
Arreglos ya registrados: {$arreglosActuales}
Disponible para marcar como arreglo: {$disponibleParaArreglos}
➡️ Solo puedes registrar máximo {$disponibleParaArreglos} prendas como arreglo.
💡 Los arreglos deben ser de prendas YA recibidas que necesitan reparación."
]);
}
// Crear ajuste
@@ -141,31 +158,12 @@ class Confeccion extends Model
'notas' => $notas,
]);
// Crear traslado de reparaciones para reflejar la salida
\App\Models\TrasladoPrenda::create([
'orden_produccion_id' => $this->orden_produccion_id,
'referencia_type' => self::class,
'referencia_id' => $this->id,
'origen' => 'confeccion',
'destino' => 'reparaciones',
'cantidad_enviada' => $cantidad,
'cantidad_recibida' => 0,
'prendas_defectuosas' => 0,
'reparaciones' => $cantidad,
'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 al confeccionista
*/
public function registerCobro(int $cantidad, float $valor, ?string $notas = null, ?int $userId = null)
{
@@ -173,46 +171,36 @@ class Confeccion extends Model
throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => 'La cantidad debe ser mayor que 0.']);
}
if (!$this->orden_produccion_id) {
throw \Illuminate\Validation\ValidationException::withMessages(['orden_produccion_id' => 'La confección debe tener una orden de producción asociada.']);
// Validar que no se exceda lo enviado
$enviada = (int) ($this->cantidad_enviada ?? 0);
if ($enviada <= 0) {
throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => 'La confección no tiene cantidad_enviada registrada.']);
}
// Buscar inventarios disponibles de esta orden de producción
$inventarios = \App\Models\InventarioPrenda::where('orden_produccion_id', $this->orden_produccion_id)
->where('cantidad_disponible', '>', 0)
->orderBy('cantidad_disponible', 'desc')
->get();
// Calcular lo que ya se ha contabilizado
$recepcionesEfectivas = (int) $this->recepciones()->sum(\DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
$arreglos = (int) $this->ajustes()->where('tipo', 'arreglo')->sum('cantidad');
$recibidoNeto = $recepcionesEfectivas - $arreglos; // Los arreglos permiten re-recepción
if ($inventarios->isEmpty()) {
throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => 'No hay inventario disponible para la orden de producción #' . $this->orden_produccion_id . '. Asegúrate de que exista inventario con cantidad_disponible > 0.']);
}
$cobrosActuales = (int) $this->cobros()->sum('cantidad');
$totalDisponible = $inventarios->sum('cantidad_disponible');
$totalContabilizado = $recibidoNeto + $cobrosActuales;
$disponible = $enviada - $totalContabilizado;
if ($totalDisponible < $cantidad) {
throw \Illuminate\Validation\ValidationException::withMessages(['cantidad' => "Solo hay {$totalDisponible} unidades disponibles en inventario. No se puede descontar {$cantidad}."]);
}
if ($cantidad > $disponible) {
throw \Illuminate\Validation\ValidationException::withMessages([
'cantidad' => "No puedes registrar {$cantidad} prendas como cobro.
// Descontar del inventario de forma distribuida si es necesario
$cantidadRestante = $cantidad;
foreach ($inventarios as $inventario) {
if ($cantidadRestante <= 0) {
break;
}
📊 Resumen:
Enviadas: {$enviada}
Recepciones efectivas: {$recepcionesEfectivas}
Arreglos (permiten re-recepción): {$arreglos}
Recibido neto: {$recibidoNeto}
Cobros registrados: {$cobrosActuales}
Disponible para cobros: {$disponible}
$aDescontar = min($cantidadRestante, $inventario->cantidad_disponible);
$inventario->cantidad_disponible -= $aDescontar;
$inventario->save();
// Si está asociado a producto, decrementar stock
if ($inventario->producto_id) {
$producto = \App\Models\Producto::find($inventario->producto_id);
if ($producto) {
$producto->decrement('stock', $aDescontar);
}
}
$cantidadRestante -= $aDescontar;
➡️ Solo puedes registrar máximo {$disponible} prendas más como cobros."
]);
}
// Crear registro de cobro
+31 -5
View File
@@ -60,20 +60,46 @@ class Recepcion extends Model
$rec->fecha_recepcion = now();
}
// Validación: no permitir sobrepasar la cantidad enviada en la referencia (considerando defectuosos)
// Validación: no permitir sobrepasar la cantidad enviada en la referencia (considerando cobros)
if ($rec->referencia) {
$referencia = $rec->referencia;
$enviada = (int) ($referencia->cantidad_enviada ?? 0);
// ya recibido efectivo (cantidad - defectuosos)
// Ya recibido efectivo (cantidad - defectuosos) - arreglos permiten re-recepción
$yaRecibido = (int) \App\Models\Recepcion::where('referencia_type', get_class($referencia))
->where('referencia_id', $referencia->id)
->sum(\DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
$efectivo = $rec->cantidad - $rec->prendas_defectuosas;
// Los arreglos se restan porque permiten re-recepción cuando regresen
$arreglos = 0;
if (method_exists($referencia, 'ajustes')) {
$arreglos = (int) $referencia->ajustes()->where('tipo', 'arreglo')->sum('cantidad');
}
if (($yaRecibido + $efectivo) > $enviada) {
throw ValidationException::withMessages(['cantidad' => "La recepción excede la cantidad pendiente (faltan: " . max(0, $enviada - $yaRecibido) . ")."]);
// Los cobros NO permiten más recepciones (son prendas que no llegaron)
$cobros = 0;
if (method_exists($referencia, 'cobros')) {
$cobros = (int) $referencia->cobros()->sum('cantidad');
}
$efectivo = $rec->cantidad - $rec->prendas_defectuosas;
$yaRecibidoNeto = $yaRecibido - $arreglos;
$maxPermitido = $enviada - $cobros;
if (($yaRecibidoNeto + $efectivo) > $maxPermitido) {
$disponible = max(0, $maxPermitido - $yaRecibidoNeto);
throw ValidationException::withMessages([
'cantidad' => "La recepción excede lo permitido.
📊 Resumen:
Enviadas: {$enviada}
Ya recibidas (neto): {$yaRecibidoNeto}
Cobros registrados: {$cobros}
Arreglos (permiten re-recepción): {$arreglos}
Disponible para recibir: {$disponible}
➡️ Solo puedes recibir {$disponible} prendas más."
]);
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
echo "=== Diagnóstico de Confección #9 ===\n\n";
$confeccion = \App\Models\Confeccion::find(9);
if (!$confeccion) {
echo "❌ No se encontró la confección con ID 9\n";
exit(1);
}
echo "✅ Confección encontrada:\n";
echo " ID: {$confeccion->id}\n";
echo " Orden Producción ID: {$confeccion->orden_produccion_id}\n";
echo " Cliente: {$confeccion->cliente->nombre}\n\n";
if (!$confeccion->orden_produccion_id) {
echo "❌ La confección NO tiene orden_produccion_id asociada\n";
exit(1);
}
echo "=== Buscando inventarios para orden_produccion_id = {$confeccion->orden_produccion_id} ===\n\n";
$inventarios = \App\Models\InventarioPrenda::where('orden_produccion_id', $confeccion->orden_produccion_id)->get();
echo "Total inventarios encontrados: {$inventarios->count()}\n\n";
if ($inventarios->isEmpty()) {
echo "❌ NO HAY INVENTARIOS para esta orden de producción\n";
echo " Esto explica el error\n\n";
// Verificar si existe la orden de producción
$op = \App\Models\OrdenProduccion::find($confeccion->orden_produccion_id);
if ($op) {
echo "✅ La orden de producción #{$op->id} existe:\n";
echo " Producto: " . ($op->producto->nombre ?? 'N/A') . "\n";
echo " Cantidad: {$op->cantidad}\n";
echo " Estado: {$op->estado}\n\n";
echo "⚠️ Necesitas crear registros de inventario para esta orden\n";
} else {
echo "❌ La orden de producción no existe\n";
}
} else {
echo "Detalles de inventarios:\n";
foreach ($inventarios as $inv) {
echo "---\n";
echo "ID: {$inv->id}\n";
echo "Cantidad terminada: {$inv->cantidad_terminada}\n";
echo "Cantidad disponible: {$inv->cantidad_disponible}\n";
echo "Estado: {$inv->estado}\n";
echo "Producto ID: {$inv->producto_id}\n";
}
$totalDisponible = $inventarios->sum('cantidad_disponible');
echo "\n✅ Total disponible: {$totalDisponible} unidades\n";
if ($totalDisponible > 0) {
echo "✅ Hay inventario suficiente para registrar cobros\n";
} else {
echo "❌ No hay inventario con cantidad_disponible > 0\n";
}
}
+72
View File
@@ -0,0 +1,72 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
echo "=== Diagnóstico de Orden de Producción #9 ===\n\n";
$op = \App\Models\OrdenProduccion::find(9);
if (!$op) {
echo "❌ No se encontró la orden de producción con ID 9\n";
exit(1);
}
echo "✅ Orden de Producción encontrada:\n";
echo " ID: {$op->id}\n";
echo " Producto: " . ($op->producto->nombre ?? 'N/A') . " (ID: {$op->producto_id})\n";
echo " Cantidad: {$op->cantidad}\n";
echo " Estado: {$op->estado}\n\n";
// Buscar confecciones asociadas
$confecciones = \App\Models\Confeccion::where('orden_produccion_id', 9)->get();
echo "Confecciones asociadas a esta OP: {$confecciones->count()}\n";
foreach ($confecciones as $conf) {
$clienteNombre = $conf->cliente ? $conf->cliente->nombre : 'Sin cliente';
echo " - Confección #{$conf->id} - Cliente: {$clienteNombre}\n";
}
echo "\n";
echo "=== Buscando inventarios para orden_produccion_id = 9 ===\n\n";
$inventarios = \App\Models\InventarioPrenda::where('orden_produccion_id', 9)->get();
echo "Total inventarios encontrados: {$inventarios->count()}\n\n";
if ($inventarios->isEmpty()) {
echo "❌ NO HAY INVENTARIOS para esta orden de producción\n";
echo " Esto explica el error al intentar registrar cobros\n\n";
echo "⚠️ Necesitas crear registros de InventarioPrenda para esta orden\n";
echo " Deberías ir a la sección de recepciones/inventario y registrar\n";
echo " las {$op->cantidad} prendas producidas\n";
} else {
echo "Detalles de inventarios:\n";
$totalTerminada = 0;
$totalDisponible = 0;
foreach ($inventarios as $inv) {
echo "---\n";
echo "ID: {$inv->id}\n";
echo "Cantidad terminada: {$inv->cantidad_terminada}\n";
echo "Cantidad disponible: {$inv->cantidad_disponible}\n";
echo "Estado: {$inv->estado}\n";
echo "Producto ID: {$inv->producto_id}\n";
echo "Fecha ingreso: {$inv->fecha_ingreso}\n";
$totalTerminada += $inv->cantidad_terminada;
$totalDisponible += $inv->cantidad_disponible;
}
echo "\n📊 Resumen:\n";
echo " Total terminada: {$totalTerminada} unidades\n";
echo " Total disponible: {$totalDisponible} unidades\n";
if ($totalDisponible > 0) {
echo "\n✅ Hay inventario suficiente para registrar cobros\n";
} else {
echo "\n❌ No hay inventario con cantidad_disponible > 0\n";
echo " Todas las prendas ya han sido cobradas/distribuidas\n";
}
}
+75
View File
@@ -0,0 +1,75 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
echo "=== Sincronización de Inventarios desde Recepciones ===\n\n";
// Buscar todas las confecciones que tienen recepciones pero cuya OP no tiene inventario
$confecciones = \App\Models\Confeccion::whereNotNull('orden_produccion_id')
->whereHas('recepciones')
->with(['ordenProduccion', 'recepciones'])
->get();
echo "Total confecciones con recepciones: {$confecciones->count()}\n\n";
$creados = 0;
$actualizados = 0;
$sinCambios = 0;
foreach ($confecciones as $confeccion) {
$op_id = $confeccion->orden_produccion_id;
$op = $confeccion->ordenProduccion;
if (!$op) {
echo "⚠️ Confección #{$confeccion->id} tiene orden_produccion_id={$op_id} pero la OP no existe\n";
continue;
}
// Sumar recepciones efectivas (cantidad - defectuosas)
$totalRecibido = $confeccion->recepciones->sum(function($rec) {
return $rec->cantidad - ($rec->prendas_defectuosas ?? 0);
});
if ($totalRecibido <= 0) {
continue;
}
// Buscar o crear inventario
$inventario = \App\Models\InventarioPrenda::where('orden_produccion_id', $op_id)->first();
if (!$inventario) {
// Crear inventario nuevo
$inventario = \App\Models\InventarioPrenda::create([
'orden_produccion_id' => $op_id,
'cantidad_terminada' => $totalRecibido,
'cantidad_disponible' => $totalRecibido,
'fecha_ingreso' => $confeccion->recepciones->first()->fecha_recepcion ?? now(),
'estado' => 'en_bodega',
]);
echo "✅ Creado inventario para OP #{$op_id} con {$totalRecibido} prendas\n";
echo " → Confección #{$confeccion->id}\n";
$creados++;
} else {
// Verificar si necesita actualización
if ($inventario->cantidad_terminada < $totalRecibido) {
$diferencia = $totalRecibido - $inventario->cantidad_terminada;
$inventario->cantidad_terminada = $totalRecibido;
$inventario->cantidad_disponible += $diferencia;
$inventario->save();
echo "✅ Actualizado inventario para OP #{$op_id}: +{$diferencia} prendas (total: {$totalRecibido})\n";
$actualizados++;
} else {
$sinCambios++;
}
}
}
echo "\n📊 Resumen:\n";
echo " Inventarios creados: {$creados}\n";
echo " Inventarios actualizados: {$actualizados}\n";
echo " Sin cambios: {$sinCambios}\n";