up
This commit is contained in:
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})",
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})",
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -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
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('ojales', function (Blueprint $table) {
|
||||
$table->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']);
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user