up
This commit is contained in:
@@ -101,78 +101,118 @@ class OjalPresillaResource extends Resource
|
||||
->visible(fn ($get) => $get('orden_produccion_id') !== null)
|
||||
->helperText('Nombre de la prenda de la orden de producción seleccionada'),
|
||||
|
||||
TextInput::make('tipo')
|
||||
->label('Tipo')
|
||||
->placeholder('Ej: Ojal, Prensilla, Botón, etc.')
|
||||
Select::make('tipo')
|
||||
->label('Tipo de Proceso')
|
||||
->options([
|
||||
'Ojal' => 'Ojal',
|
||||
'Prensilla' => 'Prensilla',
|
||||
])
|
||||
->required()
|
||||
->helperText('Especifica el tipo de accesorio'),
|
||||
->reactive()
|
||||
->afterStateUpdated(function ($state, $set, $get) {
|
||||
$opId = $get('orden_produccion_id');
|
||||
if (!$opId) return;
|
||||
|
||||
$op = \App\Models\OrdenProduccion::find($opId);
|
||||
if (!$op) return;
|
||||
|
||||
// Calcular disponibilidad según el tipo
|
||||
$cantidadRecibida = $op->confecciones->sum('cantidad_recibida');
|
||||
$arreglos = \App\Models\Ajuste::where('referencia_type', \App\Models\Confeccion::class)
|
||||
->whereHas('referencia', function($q) use ($opId) {
|
||||
$q->where('orden_produccion_id', $opId);
|
||||
})->where('tipo', 'arreglo')->sum('cantidad');
|
||||
$cobros = \App\Models\Cobro::where('referencia_type', \App\Models\Confeccion::class)
|
||||
->whereHas('referencia', function($q) use ($opId) {
|
||||
$q->where('orden_produccion_id', $opId);
|
||||
})->sum('cantidad');
|
||||
$cantidadConfecciones = $cantidadRecibida - $arreglos - $cobros;
|
||||
|
||||
if ($state === 'Ojal') {
|
||||
// Para ojales: disponible desde confección
|
||||
$yaEnviado = \App\Models\Ojal::where('orden_produccion_id', $opId)
|
||||
->sum('cantidad_enviada');
|
||||
$disponible = $cantidadConfecciones - $yaEnviado;
|
||||
$set('cantidad_enviada', max(0, $disponible));
|
||||
} else if ($state === 'Prensilla') {
|
||||
// Para prensillas: solo lo que ya terminó en ojales
|
||||
$ojalesTerminados = \App\Models\Ojal::where('orden_produccion_id', $opId)
|
||||
->where('terminada', true)
|
||||
->sum('cantidad_enviada');
|
||||
|
||||
$yaEnviado = \App\Models\Prensilla::where('orden_produccion_id', $opId)
|
||||
->sum('cantidad_enviada');
|
||||
|
||||
$disponible = $ojalesTerminados - $yaEnviado;
|
||||
$set('cantidad_enviada', max(0, $disponible));
|
||||
}
|
||||
})
|
||||
->helperText(fn ($get) => $get('tipo') === 'Prensilla'
|
||||
? '⚠️ Solo se pueden enviar prendas que hayan terminado el proceso de Ojales'
|
||||
: 'Primer proceso: las prendas vienen desde Confección'),
|
||||
|
||||
Select::make('proveedor_id')
|
||||
->label('Proveedor/Usuario Principal')
|
||||
->options(fn () => \App\Models\Proveedor::where('categoria', 'ojales_prensillas')
|
||||
->orWhere('categoria', 'empleado')
|
||||
->pluck('nombre', 'id'))
|
||||
->searchable()
|
||||
->preload()
|
||||
->nullable()
|
||||
->helperText('Principal responsable del proceso'),
|
||||
|
||||
Select::make('usuario_asignado_id')
|
||||
->label('Usuario Asignado (Operario)')
|
||||
->relationship('usuarioAsignado', 'name')
|
||||
->searchable()
|
||||
->preload()
|
||||
->nullable()
|
||||
->helperText('Usuario del sistema responsable'),
|
||||
|
||||
TextInput::make('cantidad_enviada')
|
||||
->label('Cantidad total disponible')
|
||||
->label('Cantidad')
|
||||
->numeric()
|
||||
->readOnly()
|
||||
->helperText(function ($state) {
|
||||
if (!$state) return 'Selecciona una OP primero';
|
||||
return "Máximo disponible: {$state} unidades";
|
||||
})
|
||||
->live(),
|
||||
|
||||
Repeater::make('distribuciones')
|
||||
->label('Distribución entre proveedores')
|
||||
->schema([
|
||||
Select::make('proveedor_id')
|
||||
->label('Proveedor')
|
||||
->options(fn () => \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
|
||||
$total = (int) ($get('../../cantidad_enviada') ?? 0);
|
||||
$distribuciones = $get('../../distribuciones') ?? [];
|
||||
$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})."
|
||||
]);
|
||||
}
|
||||
}),
|
||||
])
|
||||
->defaultItems(1)
|
||||
->addActionLabel('Agregar proveedor')
|
||||
->required()
|
||||
->minValue(1)
|
||||
->reactive()
|
||||
->helperText(function ($get) {
|
||||
$total = (int) ($get('cantidad_enviada') ?? 0);
|
||||
$distribuciones = $get('distribuciones') ?? [];
|
||||
$suma = 0;
|
||||
foreach ($distribuciones as $dist) {
|
||||
$suma += (int) ($dist['cantidad'] ?? 0);
|
||||
}
|
||||
$restante = $total - $suma;
|
||||
$opId = $get('orden_produccion_id');
|
||||
$tipo = $get('tipo');
|
||||
|
||||
if ($restante < 0) {
|
||||
return "⚠️ Excede el total por " . abs($restante) . " unidades";
|
||||
} elseif ($restante > 0) {
|
||||
return "Faltan {$restante} unidades por distribuir";
|
||||
if (!$opId || !$tipo) return 'Selecciona una OP y tipo primero';
|
||||
|
||||
$op = \App\Models\OrdenProduccion::find($opId);
|
||||
if (!$op) return '';
|
||||
|
||||
if ($tipo === 'Ojal') {
|
||||
$cantidadRecibida = $op->confecciones->sum('cantidad_recibida');
|
||||
$arreglos = \App\Models\Ajuste::where('referencia_type', \App\Models\Confeccion::class)
|
||||
->whereHas('referencia', function($q) use ($opId) {
|
||||
$q->where('orden_produccion_id', $opId);
|
||||
})->where('tipo', 'arreglo')->sum('cantidad');
|
||||
$cobros = \App\Models\Cobro::where('referencia_type', \App\Models\Confeccion::class)
|
||||
->whereHas('referencia', function($q) use ($opId) {
|
||||
$q->where('orden_produccion_id', $opId);
|
||||
})->sum('cantidad');
|
||||
$cantidadConfecciones = $cantidadRecibida - $arreglos - $cobros;
|
||||
$yaEnviado = \App\Models\Ojal::where('orden_produccion_id', $opId)->sum('cantidad_enviada');
|
||||
$disponible = $cantidadConfecciones - $yaEnviado;
|
||||
return "Disponible desde Confección: {$disponible}";
|
||||
} else {
|
||||
return "✓ Total distribuido correctamente";
|
||||
$ojalesTerminados = \App\Models\Ojal::where('orden_produccion_id', $opId)
|
||||
->where('terminada', true)->sum('cantidad_enviada');
|
||||
$yaEnviado = \App\Models\Prensilla::where('orden_produccion_id', $opId)->sum('cantidad_enviada');
|
||||
$disponible = $ojalesTerminados - $yaEnviado;
|
||||
return "Disponible desde Ojales terminados: {$disponible}";
|
||||
}
|
||||
})
|
||||
->live(),
|
||||
}),
|
||||
|
||||
Checkbox::make('terminada')
|
||||
->label('Marcar como terminada')
|
||||
->helperText('Al marcar terminada, se enviará automáticamente a tintorería')
|
||||
->live(),
|
||||
->label('Marcar como terminado')
|
||||
->helperText(fn ($get) => $get('tipo') === 'Prensilla'
|
||||
? '✓ Al terminar Prensillas, las prendas quedarán disponibles para Tintorería'
|
||||
: '✓ Al terminar Ojales, las prendas quedarán disponibles para Prensillas')
|
||||
->reactive(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -181,31 +221,73 @@ class OjalPresillaResource extends Resource
|
||||
return $table
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('tipo')
|
||||
->label('Tipo')
|
||||
->label('Proceso')
|
||||
->searchable()
|
||||
->sortable()
|
||||
->badge()
|
||||
->color('info'),
|
||||
->color(fn ($state) => $state === 'Ojal' ? 'info' : 'warning'),
|
||||
|
||||
Tables\Columns\TextColumn::make('ordenProduccion.numero_orden')
|
||||
->label('OP')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('ordenProduccion.prenda_modelo')
|
||||
->label('Prenda')
|
||||
->searchable()
|
||||
->limit(30)
|
||||
->toggleable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('cantidad_enviada')
|
||||
->label('Cantidad')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\TextColumn::make('proveedor.nombre')
|
||||
->label('Proveedor')
|
||||
->searchable()
|
||||
->toggleable()
|
||||
->default('Sin asignar'),
|
||||
|
||||
Tables\Columns\TextColumn::make('usuarioAsignado.name')
|
||||
->label('Operario')
|
||||
->searchable()
|
||||
->toggleable()
|
||||
->default('-'),
|
||||
|
||||
Tables\Columns\IconColumn::make('terminada')
|
||||
->boolean()
|
||||
->label('Terminada')
|
||||
->label('Terminado')
|
||||
->alignCenter()
|
||||
->sortable(),
|
||||
|
||||
Tables\Columns\BadgeColumn::make('estado_flujo')
|
||||
->label('Estado en Flujo')
|
||||
->getStateUsing(function ($record) {
|
||||
if ($record->tipo === 'Ojal') {
|
||||
if ($record->terminada) {
|
||||
// Verificar si ya se creó el proceso de prensillas
|
||||
$prensillasCreadas = \App\Models\Prensilla::where('orden_produccion_id', $record->orden_produccion_id)
|
||||
->where('cantidad_enviada', '>', 0)
|
||||
->exists();
|
||||
|
||||
return $prensillasCreadas ? 'En Prensillas' : 'Listo para Prensillas';
|
||||
}
|
||||
return 'En Proceso';
|
||||
} else {
|
||||
// Es Prensilla
|
||||
if ($record->terminada) {
|
||||
return 'Listo para Tintorería';
|
||||
}
|
||||
return 'En Proceso';
|
||||
}
|
||||
})
|
||||
->colors([
|
||||
'success' => fn ($state) => in_array($state, ['Listo para Prensillas', 'Listo para Tintorería']),
|
||||
'primary' => 'En Prensillas',
|
||||
'warning' => 'En Proceso',
|
||||
]),
|
||||
|
||||
Tables\Columns\TextColumn::make('fecha_envio')
|
||||
->date()
|
||||
->label('Fecha envío')
|
||||
@@ -219,13 +301,26 @@ class OjalPresillaResource extends Resource
|
||||
])
|
||||
->filters([
|
||||
SelectFilter::make('tipo')
|
||||
->label('Tipo'),
|
||||
->label('Tipo de Proceso')
|
||||
->options([
|
||||
'Ojal' => 'Ojal',
|
||||
'Prensilla' => 'Prensilla',
|
||||
]),
|
||||
|
||||
SelectFilter::make('terminada')
|
||||
->label('Estado')
|
||||
->options([
|
||||
'0' => 'Pendiente',
|
||||
'1' => 'Terminada',
|
||||
])
|
||||
'0' => 'En Proceso',
|
||||
'1' => 'Terminado',
|
||||
]),
|
||||
|
||||
SelectFilter::make('proveedor_id')
|
||||
->label('Proveedor')
|
||||
->relationship('proveedor', 'nombre'),
|
||||
|
||||
SelectFilter::make('usuario_asignado_id')
|
||||
->label('Operario')
|
||||
->relationship('usuarioAsignado', 'name'),
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\EditAction::make(),
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\OrdenProduccionResource\Pages;
|
||||
|
||||
use App\Filament\Resources\OrdenProduccionResource;
|
||||
use App\Models\OrdenProduccion;
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Filament\Infolists\Infolist;
|
||||
use Filament\Infolists\Components\{Section, TextEntry, Grid};
|
||||
|
||||
class VerFlujoOjalesPresillas extends Page
|
||||
{
|
||||
protected static string $resource = OrdenProduccionResource::class;
|
||||
|
||||
protected static string $view = 'filament.resources.orden-produccion-resource.pages.ver-flujo-ojales-presillas';
|
||||
|
||||
protected static ?string $title = 'Flujo de Ojales y Prensillas';
|
||||
|
||||
public OrdenProduccion $record;
|
||||
|
||||
public function mount(int | string $record): void
|
||||
{
|
||||
$this->record = OrdenProduccion::findOrFail($record);
|
||||
}
|
||||
|
||||
public function getFlujoData(): array
|
||||
{
|
||||
$op = $this->record;
|
||||
|
||||
// Datos de Confección
|
||||
$cantidadRecibidaConfeccion = $op->confecciones()
|
||||
->whereNotNull('fecha_recepcion')
|
||||
->sum('cantidad_recibida');
|
||||
|
||||
// Datos de Ojales
|
||||
$ojales = $op->ojales;
|
||||
$ojalesData = $ojales->map(function ($ojal) {
|
||||
return [
|
||||
'cantidad' => $ojal->cantidad_enviada,
|
||||
'proveedor' => $ojal->proveedor?->nombre ?? 'Sin proveedor',
|
||||
'operario' => $ojal->usuarioAsignado?->name ?? '-',
|
||||
'terminado' => $ojal->terminada,
|
||||
'fecha' => $ojal->fecha_envio?->format('d/m/Y') ?? '-',
|
||||
];
|
||||
});
|
||||
|
||||
$ojalesTerminados = $ojales->where('terminada', true)->sum('cantidad_enviada');
|
||||
$ojalesPendientes = $ojales->where('terminada', false)->sum('cantidad_enviada');
|
||||
|
||||
// Datos de Prensillas
|
||||
$prensillas = $op->prensillas;
|
||||
$prensillasData = $prensillas->map(function ($prensilla) {
|
||||
return [
|
||||
'cantidad' => $prensilla->cantidad_enviada,
|
||||
'proveedor' => $prensilla->proveedor?->nombre ?? 'Sin proveedor',
|
||||
'operario' => $prensilla->usuarioAsignado?->name ?? '-',
|
||||
'terminado' => $prensilla->terminada,
|
||||
'fecha' => $prensilla->fecha_envio?->format('d/m/Y') ?? '-',
|
||||
];
|
||||
});
|
||||
|
||||
$prensillasTerminadas = $prensillas->where('terminada', true)->sum('cantidad_enviada');
|
||||
$prensillasPendientes = $prensillas->where('terminada', false)->sum('cantidad_enviada');
|
||||
|
||||
// Verificación de ambos procesos
|
||||
$verificacion = $op->verificarProcesosCompletos();
|
||||
|
||||
return [
|
||||
'confeccion_recibida' => $cantidadRecibidaConfeccion,
|
||||
'ojales' => [
|
||||
'total' => $ojales->sum('cantidad_enviada'),
|
||||
'terminados' => $ojalesTerminados,
|
||||
'pendientes' => $ojalesPendientes,
|
||||
'registros' => $ojalesData,
|
||||
],
|
||||
'prensillas' => [
|
||||
'total' => $prensillas->sum('cantidad_enviada'),
|
||||
'terminadas' => $prensillasTerminadas,
|
||||
'pendientes' => $prensillasPendientes,
|
||||
'registros' => $prensillasData,
|
||||
],
|
||||
'verificacion' => $verificacion,
|
||||
'disponible_tintoreria' => $op->getCantidadDisponibleParaTintoreria(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -50,24 +50,20 @@ class TintoreriaResource extends Resource
|
||||
|
||||
Select::make('orden_produccion_id')
|
||||
->label('Orden de Producción')
|
||||
->options(fn () => \App\Models\OrdenProduccion::whereHas('ojales', function ($query) {
|
||||
$query->where('terminada', true);
|
||||
})->get()->mapWithKeys(function ($op) {
|
||||
// Calcular cantidad disponible de ojales terminados
|
||||
$cantidadOjalesTerminados = \App\Models\Ojal::where('orden_produccion_id', $op->id)
|
||||
->where('terminada', true)
|
||||
->sum('cantidad_enviada');
|
||||
->options(fn () => \App\Models\OrdenProduccion::all()->mapWithKeys(function ($op) {
|
||||
// Verificar que ambos procesos estén completos
|
||||
$verificacion = $op->verificarProcesosCompletos();
|
||||
|
||||
// Calcular cantidad ya enviada a tintorería
|
||||
$enviadasEnTint = \App\Models\Tintoreria::where('orden_produccion_id', $op->id)
|
||||
->whereNull('fecha_recepcion')
|
||||
->sum('cantidad_enviada');
|
||||
if (!$verificacion['completo']) {
|
||||
// Si no están completos, no mostrar o mostrar con advertencia
|
||||
return [];
|
||||
}
|
||||
|
||||
// Calcular disponible
|
||||
$disponible = $cantidadOjalesTerminados - $enviadasEnTint;
|
||||
// Calcular cantidad disponible (mínimo entre ojales y prensillas terminados - ya enviadas)
|
||||
$disponible = $op->getCantidadDisponibleParaTintoreria();
|
||||
|
||||
if ($disponible > 0) {
|
||||
return [$op->id => "{$op->numero_orden} (Disponible: {$disponible})"];
|
||||
return [$op->id => "{$op->numero_orden} (Disponible: {$disponible}) [O:{$verificacion['ojales_terminados']} P:{$verificacion['prensillas_terminadas']}]"];
|
||||
}
|
||||
return [];
|
||||
})->filter())
|
||||
@@ -77,27 +73,25 @@ class TintoreriaResource extends Resource
|
||||
->required()
|
||||
->afterStateUpdated(function ($state, $set) {
|
||||
if ($state) {
|
||||
// Calcular cantidad disponible de ojales terminados
|
||||
$cantidadOjalesTerminados = \App\Models\Ojal::where('orden_produccion_id', $state)
|
||||
->where('terminada', true)
|
||||
->sum('cantidad_enviada');
|
||||
|
||||
$enviadasEnTint = \App\Models\Tintoreria::where('orden_produccion_id', $state)
|
||||
->whereNull('fecha_recepcion')
|
||||
->sum('cantidad_enviada');
|
||||
|
||||
$pending = max(0, (int)$cantidadOjalesTerminados - (int)$enviadasEnTint);
|
||||
|
||||
$set('cantidad_enviada', $pending ?: 0);
|
||||
$set('fecha_envio', now()->toDateString());
|
||||
|
||||
$orden = \App\Models\OrdenProduccion::find($state);
|
||||
$set('prenda_nombre_info', $orden->prenda_modelo ?? 'Sin especificar');
|
||||
|
||||
if ($orden) {
|
||||
$disponible = $orden->getCantidadDisponibleParaTintoreria();
|
||||
$verificacion = $orden->verificarProcesosCompletos();
|
||||
|
||||
$set('cantidad_enviada', $disponible ?: 0);
|
||||
$set('fecha_envio', now()->toDateString());
|
||||
$set('prenda_nombre_info', $orden->prenda_modelo ?? 'Sin especificar');
|
||||
|
||||
// Información adicional sobre los procesos
|
||||
$set('info_procesos', "Ojales: {$verificacion['ojales_terminados']} | Prensillas: {$verificacion['prensillas_terminadas']}");
|
||||
}
|
||||
} else {
|
||||
$set('prenda_nombre_info', null);
|
||||
$set('info_procesos', null);
|
||||
}
|
||||
})
|
||||
->helperText('Solo se muestran órdenes con ojales/prensillas terminados'),
|
||||
->helperText('Solo se muestran órdenes con AMBOS procesos (ojales Y prensillas) terminados'),
|
||||
|
||||
TextInput::make('prenda_nombre_info')
|
||||
->label('Prenda')
|
||||
@@ -106,6 +100,13 @@ class TintoreriaResource extends Resource
|
||||
->visible(fn ($get) => $get('orden_produccion_id') !== null)
|
||||
->helperText('Nombre de la prenda de la orden de producción seleccionada'),
|
||||
|
||||
TextInput::make('info_procesos')
|
||||
->label('Estado de Procesos')
|
||||
->disabled()
|
||||
->dehydrated(false)
|
||||
->visible(fn ($get) => $get('orden_produccion_id') !== null)
|
||||
->helperText('Cantidad terminada en cada proceso'),
|
||||
|
||||
TextInput::make('tipo_proceso')
|
||||
->label('Tipo de proceso')
|
||||
->required(),
|
||||
|
||||
@@ -43,7 +43,7 @@ class PrendasPendientesWidget extends BaseWidget
|
||||
->label('Pend. Confección')
|
||||
->alignCenter()
|
||||
->state(function (OrdenProduccion $record): int {
|
||||
// Recibidas en confección - Enviadas a ojales
|
||||
// Recibidas en confección - Enviadas a ojales y prensillas
|
||||
$recibidas = $record->confecciones()
|
||||
->whereNotNull('fecha_recepcion')
|
||||
->sum('cantidad_recibida');
|
||||
@@ -62,8 +62,9 @@ class PrendasPendientesWidget extends BaseWidget
|
||||
$disponiblesConfeccion = $recibidas - $arreglos - $cobros;
|
||||
|
||||
$enviadasOjales = $record->ojales()->sum('cantidad_enviada');
|
||||
$enviadasPrensillas = $record->prensillas()->sum('cantidad_enviada');
|
||||
|
||||
return max(0, $disponiblesConfeccion - $enviadasOjales);
|
||||
return max(0, $disponiblesConfeccion - $enviadasOjales - $enviadasPrensillas);
|
||||
})
|
||||
->color(fn ($state) => $state > 0 ? 'warning' : 'gray')
|
||||
->weight(fn ($state) => $state > 0 ? 'bold' : 'normal'),
|
||||
@@ -71,17 +72,51 @@ class PrendasPendientesWidget extends BaseWidget
|
||||
Tables\Columns\TextColumn::make('pendiente_ojales')
|
||||
->label('Pend. Ojales')
|
||||
->alignCenter()
|
||||
->state(function (OrdenProduccion $record): int {
|
||||
->state(function (OrdenProduccion $record): string {
|
||||
// Enviadas a ojales - Terminadas en ojales
|
||||
$enviadas = $record->ojales()->sum('cantidad_enviada');
|
||||
$terminadas = $record->ojales()
|
||||
->where('terminada', true)
|
||||
->sum('cantidad_enviada');
|
||||
$pendientes = max(0, $enviadas - $terminadas);
|
||||
|
||||
return max(0, $enviadas - $terminadas);
|
||||
return "{$terminadas}/{$enviadas}";
|
||||
})
|
||||
->color(fn ($state) => $state > 0 ? 'warning' : 'gray')
|
||||
->weight(fn ($state) => $state > 0 ? 'bold' : 'normal'),
|
||||
->color(function (OrdenProduccion $record) {
|
||||
$enviadas = $record->ojales()->sum('cantidad_enviada');
|
||||
$terminadas = $record->ojales()->where('terminada', true)->sum('cantidad_enviada');
|
||||
return $terminadas < $enviadas ? 'warning' : ($terminadas > 0 ? 'success' : 'gray');
|
||||
})
|
||||
->weight(function (OrdenProduccion $record) {
|
||||
$enviadas = $record->ojales()->sum('cantidad_enviada');
|
||||
$terminadas = $record->ojales()->where('terminada', true)->sum('cantidad_enviada');
|
||||
return $terminadas < $enviadas ? 'bold' : 'normal';
|
||||
})
|
||||
->tooltip('Terminadas / Total Enviadas'),
|
||||
|
||||
Tables\Columns\TextColumn::make('pendiente_prensillas')
|
||||
->label('Pend. Prensillas')
|
||||
->alignCenter()
|
||||
->state(function (OrdenProduccion $record): string {
|
||||
$enviadas = $record->prensillas()->sum('cantidad_enviada');
|
||||
$terminadas = $record->prensillas()
|
||||
->where('terminada', true)
|
||||
->sum('cantidad_enviada');
|
||||
$pendientes = max(0, $enviadas - $terminadas);
|
||||
|
||||
return "{$terminadas}/{$enviadas}";
|
||||
})
|
||||
->color(function (OrdenProduccion $record) {
|
||||
$enviadas = $record->prensillas()->sum('cantidad_enviada');
|
||||
$terminadas = $record->prensillas()->where('terminada', true)->sum('cantidad_enviada');
|
||||
return $terminadas < $enviadas ? 'warning' : ($terminadas > 0 ? 'success' : 'gray');
|
||||
})
|
||||
->weight(function (OrdenProduccion $record) {
|
||||
$enviadas = $record->prensillas()->sum('cantidad_enviada');
|
||||
$terminadas = $record->prensillas()->where('terminada', true)->sum('cantidad_enviada');
|
||||
return $terminadas < $enviadas ? 'bold' : 'normal';
|
||||
})
|
||||
->tooltip('Terminadas / Total Enviadas'),
|
||||
|
||||
Tables\Columns\TextColumn::make('pendiente_tintoreria')
|
||||
->label('Pend. Tintorería')
|
||||
|
||||
@@ -10,6 +10,7 @@ class Ojal extends Model
|
||||
|
||||
protected $fillable = [
|
||||
'proveedor_id',
|
||||
'usuario_asignado_id',
|
||||
'orden_produccion_id',
|
||||
'tipo',
|
||||
'fecha_envio',
|
||||
@@ -30,6 +31,11 @@ class Ojal extends Model
|
||||
return $this->belongsTo(Proveedor::class);
|
||||
}
|
||||
|
||||
public function usuarioAsignado()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'usuario_asignado_id');
|
||||
}
|
||||
|
||||
public function ordenProduccion()
|
||||
{
|
||||
return $this->belongsTo(OrdenProduccion::class);
|
||||
|
||||
@@ -83,7 +83,58 @@ class OrdenProduccion extends Model
|
||||
return $this->belongsTo(\App\Models\Producto::class, 'producto_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica si ambos procesos (ojales y prensillas) están completos para una cantidad específica
|
||||
*
|
||||
* @param int $cantidad Cantidad de prendas a verificar
|
||||
* @return array ['completo' => bool, 'ojales_terminados' => int, 'prensillas_terminadas' => int, 'minimo' => int]
|
||||
*/
|
||||
public function verificarProcesosCom
|
||||
|
||||
pletos(int $cantidad = null): array
|
||||
{
|
||||
$cantidadVerificar = $cantidad ?? $this->cantidad_total;
|
||||
|
||||
// Cantidad de ojales terminados
|
||||
$ojalesTerminados = $this->ojales()
|
||||
->where('terminada', true)
|
||||
->sum('cantidad_enviada');
|
||||
|
||||
// Cantidad de prensillas terminadas
|
||||
$prensillasTerminadas = $this->prensillas()
|
||||
->where('terminada', true)
|
||||
->sum('cantidad_enviada');
|
||||
|
||||
// El mínimo entre ambos procesos
|
||||
$minimoCompleto = min($ojalesTerminados, $prensillasTerminadas);
|
||||
|
||||
// Están completos si el mínimo cubre la cantidad requerida
|
||||
$completo = $minimoCompleto >= $cantidadVerificar;
|
||||
|
||||
return [
|
||||
'completo' => $completo,
|
||||
'ojales_terminados' => $ojalesTerminados,
|
||||
'prensillas_terminadas' => $prensillasTerminadas,
|
||||
'minimo_completo' => $minimoCompleto,
|
||||
'cantidad_requerida' => $cantidadVerificar,
|
||||
'faltante_ojales' => max(0, $cantidadVerificar - $ojalesTerminados),
|
||||
'faltante_prensillas' => max(0, $cantidadVerificar - $prensillasTerminadas),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene la cantidad disponible para enviar a tintorería
|
||||
* (solo las prendas que tienen ambos procesos completos)
|
||||
*/
|
||||
public function getCantidadDisponibleParaTintoreria(): int
|
||||
{
|
||||
$verificacion = $this->verificarProcesosCompletos();
|
||||
|
||||
// Ya enviadas a tintorería
|
||||
$enviadasTintoreria = $this->tintorerias()->sum('cantidad_enviada');
|
||||
|
||||
return max(0, $verificacion['minimo_completo'] - $enviadasTintoreria);
|
||||
}
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ class Prensilla extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'proveedor_id',
|
||||
'usuario_asignado_id',
|
||||
'orden_produccion_id',
|
||||
'tipo',
|
||||
'fecha_envio',
|
||||
@@ -28,6 +29,11 @@ class Prensilla extends Model
|
||||
return $this->belongsTo(Proveedor::class);
|
||||
}
|
||||
|
||||
public function usuarioAsignado()
|
||||
{
|
||||
return $this->belongsTo(\App\Models\User::class, 'usuario_asignado_id');
|
||||
}
|
||||
|
||||
public function ordenProduccion()
|
||||
{
|
||||
return $this->belongsTo(OrdenProduccion::class);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// Agregar campo usuario_asignado a ojales
|
||||
if (!Schema::hasColumn('ojales', 'usuario_asignado_id')) {
|
||||
Schema::table('ojales', function (Blueprint $table) {
|
||||
$table->foreignId('usuario_asignado_id')->nullable()->after('proveedor_id')->constrained('users')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
|
||||
// Agregar campo usuario_asignado a prensillas
|
||||
if (!Schema::hasColumn('prensillas', 'usuario_asignado_id')) {
|
||||
Schema::table('prensillas', function (Blueprint $table) {
|
||||
$table->foreignId('usuario_asignado_id')->nullable()->after('proveedor_id')->constrained('users')->nullOnDelete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('ojales', function (Blueprint $table) {
|
||||
$table->dropForeign(['usuario_asignado_id']);
|
||||
$table->dropColumn('usuario_asignado_id');
|
||||
});
|
||||
|
||||
Schema::table('prensillas', function (Blueprint $table) {
|
||||
$table->dropForeign(['usuario_asignado_id']);
|
||||
$table->dropColumn('usuario_asignado_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
<x-filament-panels::page>
|
||||
@php
|
||||
$data = $this->getFlujoData();
|
||||
$op = $this->record;
|
||||
@endphp
|
||||
|
||||
<div class="space-y-6">
|
||||
{{-- Información de la Orden --}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<h2 class="text-xl font-bold mb-4">{{ $op->numero_orden }} - {{ $op->prenda_modelo }}</h2>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Total OP</p>
|
||||
<p class="text-2xl font-bold">{{ $op->cantidad_total }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Recibidas Confección</p>
|
||||
<p class="text-2xl font-bold text-blue-600">{{ $data['confeccion_recibida'] }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Disponible para Tintorería</p>
|
||||
<p class="text-2xl font-bold text-green-600">{{ $data['disponible_tintoreria'] }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">Estado</p>
|
||||
@if($data['verificacion']['completo'])
|
||||
<p class="text-2xl font-bold text-green-600">✓ Completo</p>
|
||||
@else
|
||||
<p class="text-2xl font-bold text-yellow-600">En Proceso</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Proceso de Ojales --}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-bold">🔘 Proceso de Ojales</h3>
|
||||
<div class="flex gap-4 text-sm">
|
||||
<span class="text-green-600 font-semibold">Terminados: {{ $data['ojales']['terminados'] }}</span>
|
||||
<span class="text-yellow-600 font-semibold">Pendientes: {{ $data['ojales']['pendientes'] }}</span>
|
||||
<span class="text-gray-600 font-semibold">Total: {{ $data['ojales']['total'] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($data['ojales']['registros']->count() > 0)
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-4 py-2 text-left">Cantidad</th>
|
||||
<th class="px-4 py-2 text-left">Proveedor</th>
|
||||
<th class="px-4 py-2 text-left">Operario</th>
|
||||
<th class="px-4 py-2 text-left">Fecha</th>
|
||||
<th class="px-4 py-2 text-center">Estado</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@foreach($data['ojales']['registros'] as $ojal)
|
||||
<tr>
|
||||
<td class="px-4 py-2 font-semibold">{{ $ojal['cantidad'] }}</td>
|
||||
<td class="px-4 py-2">{{ $ojal['proveedor'] }}</td>
|
||||
<td class="px-4 py-2">{{ $ojal['operario'] }}</td>
|
||||
<td class="px-4 py-2">{{ $ojal['fecha'] }}</td>
|
||||
<td class="px-4 py-2 text-center">
|
||||
@if($ojal['terminado'])
|
||||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium text-green-700 bg-green-100 rounded-full">
|
||||
✓ Terminado
|
||||
</span>
|
||||
@else
|
||||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium text-yellow-700 bg-yellow-100 rounded-full">
|
||||
En Proceso
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-gray-500 text-center py-4">No hay registros de ojales</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Proceso de Prensillas --}}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-bold">✂️ Proceso de Prensillas</h3>
|
||||
<div class="flex gap-4 text-sm">
|
||||
<span class="text-green-600 font-semibold">Terminadas: {{ $data['prensillas']['terminadas'] }}</span>
|
||||
<span class="text-yellow-600 font-semibold">Pendientes: {{ $data['prensillas']['pendientes'] }}</span>
|
||||
<span class="text-gray-600 font-semibold">Total: {{ $data['prensillas']['total'] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($data['prensillas']['registros']->count() > 0)
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th class="px-4 py-2 text-left">Cantidad</th>
|
||||
<th class="px-4 py-2 text-left">Proveedor</th>
|
||||
<th class="px-4 py-2 text-left">Operario</th>
|
||||
<th class="px-4 py-2 text-left">Fecha</th>
|
||||
<th class="px-4 py-2 text-center">Estado</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
@foreach($data['prensillas']['registros'] as $prensilla)
|
||||
<tr>
|
||||
<td class="px-4 py-2 font-semibold">{{ $prensilla['cantidad'] }}</td>
|
||||
<td class="px-4 py-2">{{ $prensilla['proveedor'] }}</td>
|
||||
<td class="px-4 py-2">{{ $prensilla['operario'] }}</td>
|
||||
<td class="px-4 py-2">{{ $prensilla['fecha'] }}</td>
|
||||
<td class="px-4 py-2 text-center">
|
||||
@if($prensilla['terminado'])
|
||||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium text-green-700 bg-green-100 rounded-full">
|
||||
✓ Terminado
|
||||
</span>
|
||||
@else
|
||||
<span class="inline-flex items-center px-2 py-1 text-xs font-medium text-yellow-700 bg-yellow-100 rounded-full">
|
||||
En Proceso
|
||||
</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-gray-500 text-center py-4">No hay registros de prensillas</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Resumen de Verificación --}}
|
||||
<div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-6">
|
||||
<h3 class="text-lg font-bold mb-4">📊 Estado del Flujo</h3>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">Flujo Secuencial:</p>
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<span class="px-3 py-1 bg-blue-100 text-blue-800 rounded font-medium">Confección</span>
|
||||
<span>→</span>
|
||||
<span class="px-3 py-1 {{ $data['ojales']['terminados'] > 0 ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' }} rounded font-medium">
|
||||
Ojales {{ $data['ojales']['terminados'] > 0 ? '✓' : '' }}
|
||||
</span>
|
||||
<span>→</span>
|
||||
<span class="px-3 py-1 {{ $data['prensillas']['terminadas'] > 0 ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' }} rounded font-medium">
|
||||
Prensillas {{ $data['prensillas']['terminadas'] > 0 ? '✓' : '' }}
|
||||
</span>
|
||||
<span>→</span>
|
||||
<span class="px-3 py-1 {{ $data['disponible_tintoreria'] > 0 ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800' }} rounded font-medium">
|
||||
Tintorería {{ $data['disponible_tintoreria'] > 0 ? '✓' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-2">Mensajes:</p>
|
||||
@if($data['verificacion']['faltante_ojales'] > 0)
|
||||
<p class="text-sm text-yellow-600">⚠️ Faltan {{ $data['verificacion']['faltante_ojales'] }} prendas por terminar en Ojales</p>
|
||||
@endif
|
||||
@if($data['verificacion']['faltante_prensillas'] > 0)
|
||||
<p class="text-sm text-yellow-600">⚠️ Faltan {{ $data['verificacion']['faltante_prensillas'] }} prendas por terminar en Prensillas</p>
|
||||
@endif
|
||||
@if($data['verificacion']['completo'])
|
||||
<p class="text-sm text-green-600">✓ Ambos procesos completados. {{ $data['disponible_tintoreria'] }} prendas listas para Tintorería</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-filament-panels::page>
|
||||
Reference in New Issue
Block a user