This commit is contained in:
Lizandro Guarnizo
2026-02-04 15:08:34 -05:00
parent 8ef6beedcc
commit b92c6ba992
3 changed files with 434 additions and 0 deletions
@@ -0,0 +1,276 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\SeguimientoProcesoResource\Pages;
use App\Models\OrdenProduccion;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
class SeguimientoProcesoResource extends Resource
{
protected static ?string $model = OrdenProduccion::class;
protected static ?string $navigationIcon = 'heroicon-o-chart-bar';
protected static ?string $navigationLabel = 'Seguimiento de Procesos';
protected static ?string $modelLabel = 'Seguimiento de Proceso';
protected static ?string $pluralModelLabel = 'Seguimiento de Procesos';
protected static ?string $navigationGroup = 'Reportes';
protected static ?int $navigationSort = 10;
public static function canCreate(): bool
{
return false;
}
public static function form(Form $form): Form
{
return $form->schema([]);
}
public static function table(Table $table): Table
{
return $table
->query(
OrdenProduccion::query()
->orderBy('created_at', 'desc')
)
->columns([
Tables\Columns\TextColumn::make('numero_orden')
->label('Orden')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('prenda_modelo')
->label('Prenda')
->searchable()
->limit(30),
Tables\Columns\TextColumn::make('referencia')
->label('Referencia')
->searchable()
->toggleable(),
Tables\Columns\TextColumn::make('cantidad_total')
->label('Total')
->alignCenter()
->sortable(),
Tables\Columns\TextColumn::make('estado')
->badge()
->colors([
'danger' => 'cancelada',
'warning' => fn ($state) => in_array($state, ['pendiente', 'en_confeccion', 'en_tintoreria', 'en_acabados']),
'success' => 'finalizada',
'primary' => 'en_proceso',
]),
Tables\Columns\TextColumn::make('confeccion')
->label('📦 Confección')
->alignCenter()
->state(function (OrdenProduccion $record): string {
$enviadas = $record->confecciones()->sum('cantidad_enviada');
$recibidas = $record->confecciones()
->whereNotNull('fecha_recepcion')
->sum('cantidad_recibida');
// Calcular disponibles (recibidas - arreglos - cobros - enviadas a ojales)
$arreglos = \App\Models\Ajuste::where('referencia_type', \App\Models\Confeccion::class)
->whereHas('referencia', function($q) use ($record) {
$q->where('orden_produccion_id', $record->id);
})->where('tipo', 'arreglo')->sum('cantidad');
$cobros = \App\Models\Cobro::where('referencia_type', \App\Models\Confeccion::class)
->whereHas('referencia', function($q) use ($record) {
$q->where('orden_produccion_id', $record->id);
})->sum('cantidad');
$disponiblesConfeccion = $recibidas - $arreglos - $cobros;
$enviadasOjales = $record->ojales()->sum('cantidad_enviada');
$pendientes = max(0, $disponiblesConfeccion - $enviadasOjales);
return "E:{$enviadas} R:{$recibidas} P:{$pendientes}";
})
->color(fn (OrdenProduccion $record) => $this->getPendienteConfeccion($record) > 0 ? 'warning' : 'gray')
->tooltip('E=Enviadas, R=Recibidas, P=Pendientes'),
Tables\Columns\TextColumn::make('ojales')
->label('🔘 Ojales')
->alignCenter()
->state(function (OrdenProduccion $record): string {
$enviadas = $record->ojales()->sum('cantidad_enviada');
$terminadas = $record->ojales()->where('terminada', true)->sum('cantidad_enviada');
$pendientes = max(0, $enviadas - $terminadas);
return "E:{$enviadas} T:{$terminadas} P:{$pendientes}";
})
->color(fn (OrdenProduccion $record) => $this->getPendienteOjales($record) > 0 ? 'warning' : 'gray')
->tooltip('E=Enviadas, T=Terminadas, P=Pendientes'),
Tables\Columns\TextColumn::make('tintoreria')
->label('🎨 Tintorería')
->alignCenter()
->state(function (OrdenProduccion $record): string {
$enviadas = $record->tintorerias()->sum('cantidad_enviada');
$recepciones = 0;
foreach ($record->tintorerias as $tintoreria) {
$recepciones += (int) $tintoreria->recepciones()
->sum(DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
}
$pendientes = max(0, $enviadas - $recepciones);
return "E:{$enviadas} R:{$recepciones} P:{$pendientes}";
})
->color(fn (OrdenProduccion $record) => $this->getPendienteTintoreria($record) > 0 ? 'warning' : 'gray')
->tooltip('E=Enviadas, R=Recibidas, P=Pendientes'),
Tables\Columns\TextColumn::make('acabados')
->label('✨ Acabados')
->alignCenter()
->state(function (OrdenProduccion $record): string {
// Disponibles desde tintorería
$disponibles = 0;
foreach ($record->tintorerias as $tintoreria) {
$recep = (int) $tintoreria->recepciones()
->sum(DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
$reprocesos = (int) $tintoreria->ajustes()
->where('tipo', 'reproceso')->sum('cantidad');
$cobros = (int) $tintoreria->cobros()->sum('cantidad');
$disponibles += ($recep - $reprocesos - $cobros);
}
$enviadasAcabados = $record->procesosAcabado()->sum('cantidad_enviada');
$recibidasAcabados = $record->procesosAcabado()
->whereNotNull('fecha_recepcion')
->sum('cantidad_recibida');
$pendientes = max(0, $disponibles - $enviadasAcabados);
return "D:{$disponibles} E:{$enviadasAcabados} P:{$pendientes}";
})
->color(fn (OrdenProduccion $record) => $this->getPendienteAcabados($record) > 0 ? 'warning' : 'gray')
->tooltip('D=Disponibles, E=Enviadas, P=Pendientes'),
Tables\Columns\TextColumn::make('bodega')
->label('📦 Bodega')
->alignCenter()
->state(function (OrdenProduccion $record): string {
$total = $record->inventarioPrenda()->sum('cantidad_terminada');
$disponible = $record->inventarioPrenda()->sum('cantidad_disponible');
return "T:{$total} D:{$disponible}";
})
->color(fn (OrdenProduccion $record) => $this->getEnBodega($record) > 0 ? 'success' : 'gray')
->tooltip('T=Total, D=Disponible'),
])
->filters([
Tables\Filters\SelectFilter::make('estado')
->options([
'pendiente' => 'Pendiente',
'en_proceso' => 'En Proceso',
'en_confeccion' => 'En Confección',
'en_tintoreria' => 'En Tintorería',
'en_acabados' => 'En Acabados',
'finalizada' => 'Finalizada',
'cancelada' => 'Cancelada',
]),
Tables\Filters\Filter::make('con_pendientes')
->label('Solo con pendientes')
->query(fn (Builder $query) => $query->whereIn('estado', ['en_proceso', 'en_confeccion', 'en_tintoreria', 'en_acabados'])),
])
->actions([])
->bulkActions([])
->defaultSort('created_at', 'desc')
->paginated([10, 25, 50, 100])
->poll('30s');
}
protected static function getPendienteConfeccion(OrdenProduccion $record): int
{
$recibidas = $record->confecciones()
->whereNotNull('fecha_recepcion')
->sum('cantidad_recibida');
$arreglos = \App\Models\Ajuste::where('referencia_type', \App\Models\Confeccion::class)
->whereHas('referencia', function($q) use ($record) {
$q->where('orden_produccion_id', $record->id);
})->where('tipo', 'arreglo')->sum('cantidad');
$cobros = \App\Models\Cobro::where('referencia_type', \App\Models\Confeccion::class)
->whereHas('referencia', function($q) use ($record) {
$q->where('orden_produccion_id', $record->id);
})->sum('cantidad');
$disponiblesConfeccion = $recibidas - $arreglos - $cobros;
$enviadasOjales = $record->ojales()->sum('cantidad_enviada');
return max(0, $disponiblesConfeccion - $enviadasOjales);
}
protected static function getPendienteOjales(OrdenProduccion $record): int
{
$enviadas = $record->ojales()->sum('cantidad_enviada');
$terminadas = $record->ojales()->where('terminada', true)->sum('cantidad_enviada');
return max(0, $enviadas - $terminadas);
}
protected static function getPendienteTintoreria(OrdenProduccion $record): int
{
$enviadas = $record->tintorerias()->sum('cantidad_enviada');
$recepciones = 0;
foreach ($record->tintorerias as $tintoreria) {
$recepciones += (int) $tintoreria->recepciones()
->sum(DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
}
return max(0, $enviadas - $recepciones);
}
protected static function getPendienteAcabados(OrdenProduccion $record): int
{
$disponibles = 0;
foreach ($record->tintorerias as $tintoreria) {
$recep = (int) $tintoreria->recepciones()
->sum(DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
$reprocesos = (int) $tintoreria->ajustes()
->where('tipo', 'reproceso')->sum('cantidad');
$cobros = (int) $tintoreria->cobros()->sum('cantidad');
$disponibles += ($recep - $reprocesos - $cobros);
}
$enviadasAcabados = $record->procesosAcabado()->sum('cantidad_enviada');
return max(0, $disponibles - $enviadasAcabados);
}
protected static function getEnBodega(OrdenProduccion $record): int
{
return $record->inventarioPrenda()->sum('cantidad_disponible');
}
public static function getRelations(): array
{
return [];
}
public static function getPages(): array
{
return [
'index' => Pages\ListSeguimientoProcesos::route('/'),
];
}
}
@@ -0,0 +1,16 @@
<?php
namespace App\Filament\Resources\SeguimientoProcesoResource\Pages;
use App\Filament\Resources\SeguimientoProcesoResource;
use Filament\Resources\Pages\ListRecords;
class ListSeguimientoProcesos extends ListRecords
{
protected static string $resource = SeguimientoProcesoResource::class;
protected function getHeaderActions(): array
{
return [];
}
}
@@ -0,0 +1,142 @@
<?php
namespace App\Filament\Widgets;
use App\Models\OrdenProduccion;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Widgets\TableWidget as BaseWidget;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
class PrendasPendientesWidget extends BaseWidget
{
protected static ?int $sort = 2;
protected int | string | array $columnSpan = 'full';
protected static ?string $heading = 'Prendas Pendientes por Proceso';
public function table(Table $table): Table
{
return $table
->query(
OrdenProduccion::query()
->whereIn('estado', ['en_proceso', 'en_confeccion', 'en_tintoreria', 'en_acabados'])
->orderBy('created_at', 'desc')
)
->columns([
Tables\Columns\TextColumn::make('numero_orden')
->label('Orden')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('prenda_modelo')
->label('Prenda')
->searchable()
->limit(30),
Tables\Columns\TextColumn::make('cantidad_total')
->label('Total OP')
->alignCenter(),
Tables\Columns\TextColumn::make('pendiente_confeccion')
->label('Pend. Confección')
->alignCenter()
->state(function (OrdenProduccion $record): int {
// Recibidas en confección - Enviadas a ojales
$recibidas = $record->confecciones()
->whereNotNull('fecha_recepcion')
->sum('cantidad_recibida');
// Restamos arreglos y cobros de confección
$arreglos = \App\Models\Ajuste::where('referencia_type', \App\Models\Confeccion::class)
->whereHas('referencia', function($q) use ($record) {
$q->where('orden_produccion_id', $record->id);
})->where('tipo', 'arreglo')->sum('cantidad');
$cobros = \App\Models\Cobro::where('referencia_type', \App\Models\Confeccion::class)
->whereHas('referencia', function($q) use ($record) {
$q->where('orden_produccion_id', $record->id);
})->sum('cantidad');
$disponiblesConfeccion = $recibidas - $arreglos - $cobros;
$enviadasOjales = $record->ojales()->sum('cantidad_enviada');
return max(0, $disponiblesConfeccion - $enviadasOjales);
})
->color(fn ($state) => $state > 0 ? 'warning' : 'gray')
->weight(fn ($state) => $state > 0 ? 'bold' : 'normal'),
Tables\Columns\TextColumn::make('pendiente_ojales')
->label('Pend. Ojales')
->alignCenter()
->state(function (OrdenProduccion $record): int {
// Enviadas a ojales - Terminadas en ojales
$enviadas = $record->ojales()->sum('cantidad_enviada');
$terminadas = $record->ojales()
->where('terminada', true)
->sum('cantidad_enviada');
return max(0, $enviadas - $terminadas);
})
->color(fn ($state) => $state > 0 ? 'warning' : 'gray')
->weight(fn ($state) => $state > 0 ? 'bold' : 'normal'),
Tables\Columns\TextColumn::make('pendiente_tintoreria')
->label('Pend. Tintorería')
->alignCenter()
->state(function (OrdenProduccion $record): int {
// Enviadas a tintorería - Recibidas de tintorería
$enviadas = $record->tintorerias()
->sum('cantidad_enviada');
$recepciones = 0;
foreach ($record->tintorerias as $tintoreria) {
$recepciones += (int) $tintoreria->recepciones()
->sum(DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
}
return max(0, $enviadas - $recepciones);
})
->color(fn ($state) => $state > 0 ? 'warning' : 'gray')
->weight(fn ($state) => $state > 0 ? 'bold' : 'normal'),
Tables\Columns\TextColumn::make('pendiente_acabados')
->label('Pend. Acabados')
->alignCenter()
->state(function (OrdenProduccion $record): int {
// Disponibles desde tintorería - Enviadas a acabados
$recepciones = 0;
foreach ($record->tintorerias as $tintoreria) {
$recep = (int) $tintoreria->recepciones()
->sum(DB::raw('cantidad - COALESCE(prendas_defectuosas, 0)'));
$reprocesos = (int) $tintoreria->ajustes()
->where('tipo', 'reproceso')->sum('cantidad');
$cobros = (int) $tintoreria->cobros()->sum('cantidad');
$recepciones += ($recep - $reprocesos - $cobros);
}
$enviadasAcabados = $record->procesosAcabado()
->sum('cantidad_enviada');
return max(0, $recepciones - $enviadasAcabados);
})
->color(fn ($state) => $state > 0 ? 'warning' : 'gray')
->weight(fn ($state) => $state > 0 ? 'bold' : 'normal'),
Tables\Columns\TextColumn::make('en_bodega')
->label('En Bodega')
->alignCenter()
->state(function (OrdenProduccion $record): int {
// Inventario disponible
return $record->inventarioPrenda()
->sum('cantidad_disponible');
})
->color(fn ($state) => $state > 0 ? 'success' : 'gray')
->weight(fn ($state) => $state > 0 ? 'bold' : 'normal'),
])
->paginated([10, 25, 50])
->defaultPaginationPageOption(25);
}
}