From 66793f6b2e018df359b806b8366c85e14ce4e4a6 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Thu, 26 Feb 2026 12:50:54 +0700 Subject: [PATCH] feat: Decouple feed deliveries from schedules, add soft deletes to deliveries, and dynamically schedule feed processing by time. --- app/Console/Commands/ProcessFeedSchedule.php | 26 +++++----- .../FeedDeliveries/FeedDeliveryResource.php | 48 ++++++++++++++----- .../Pages/ManageFeedDeliveries.php | 2 +- .../FeedSchedules/FeedScheduleResource.php | 6 +-- app/Models/FeedDelivery.php | 8 +--- app/Models/FeedSchedule.php | 6 --- ...26_000002_create_feed_deliveries_table.php | 5 +- routes/console.php | 13 ++++- 8 files changed, 71 insertions(+), 43 deletions(-) diff --git a/app/Console/Commands/ProcessFeedSchedule.php b/app/Console/Commands/ProcessFeedSchedule.php index adff12b..f87ab63 100644 --- a/app/Console/Commands/ProcessFeedSchedule.php +++ b/app/Console/Commands/ProcessFeedSchedule.php @@ -8,25 +8,26 @@ class ProcessFeedSchedule extends Command { - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = 'feed:schedule-process'; + protected $signature = 'feed:schedule-process {time?}'; /** * The console command description. * * @var string */ - protected $description = 'Process any pending feed schedules and adjust stock accordingly'; + protected $description = 'Process any pending feed schedules for a specific time and adjust stock accordingly'; public function handle(): int { - $now = now(); + $time = $this->argument('time') ?? now()->format('H:i'); - $schedules = FeedSchedule::all(); + $schedules = FeedSchedule::whereTime('scheduled_at', $time)->get(); + + if ($schedules->isEmpty()) { + $this->info("No schedules found for time: {$time}"); + + return 0; + } foreach ($schedules as $schedule) { $feed = $schedule->feed; @@ -36,15 +37,14 @@ public function handle(): int } FeedDelivery::create([ - 'feed_schedule_id' => $schedule->id, 'feed_id' => $schedule->feed_id, 'quantity' => $schedule->quantity, - 'delivered_at' => $now, + 'delivered_at' => now(), ]); - - $schedule->save(); } + $this->info('Processed '.$schedules->count()." feed schedules for time: {$time}"); + return 0; } } diff --git a/app/Filament/Resources/Manage/FeedDeliveries/FeedDeliveryResource.php b/app/Filament/Resources/Manage/FeedDeliveries/FeedDeliveryResource.php index 3ffc775..9273e2d 100644 --- a/app/Filament/Resources/Manage/FeedDeliveries/FeedDeliveryResource.php +++ b/app/Filament/Resources/Manage/FeedDeliveries/FeedDeliveryResource.php @@ -1,6 +1,6 @@ components([ + Select::make('feed_id') + ->label('Pakan') + ->relationship('feed', 'name') + ->required() + ->searchable() + ->preload(), + + TextInput::make('quantity') + ->label('Kuantitas') + ->placeholder('0') + ->autocomplete(false) + ->required() + ->currencyMask(thousandSeparator: '.', decimalSeparator: ',', precision: 0) + ->dehydrateStateUsing(fn ($state) => (int) str()->replace(',', '', (string) ($state ?? 0))) + ->helperText('Satuan kuantitas pakan sesuai dengan satuan yang ada di menu pakan.'), + ]) + ->columns(1); + } + public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('feed.name') ->label('Pakan') - ->sortable(), + ->sortable() + ->searchable(), TextColumn::make('quantity') - ->label('Qty') - ->numeric(), + ->label('Kuantitas') + ->numeric(decimalPlaces: 0) + ->suffix(fn (FeedDelivery $record) => ' '.$record->feed?->unit?->alias) + ->searchable(), TextColumn::make('delivered_at') ->label('Tanggal Disalurkan') - ->dateTime('d M Y H:i') - ->sortable(), - - TextColumn::make('schedule.id') - ->label('Jadwal ID') - ->sortable(), + ->dateTime('l, d M Y H:i') + ->sortable() + ->searchable(), ...TimestampColumns::make(), ]) diff --git a/app/Filament/Resources/Manage/FeedDeliveries/Pages/ManageFeedDeliveries.php b/app/Filament/Resources/Manage/FeedDeliveries/Pages/ManageFeedDeliveries.php index 8a50cf3..c80067e 100644 --- a/app/Filament/Resources/Manage/FeedDeliveries/Pages/ManageFeedDeliveries.php +++ b/app/Filament/Resources/Manage/FeedDeliveries/Pages/ManageFeedDeliveries.php @@ -2,7 +2,7 @@ namespace App\Filament\Resources\Manage\FeedDeliveries\Pages; -use App\Filament\Resources\Manage\Deliveries\FeedDeliveryResource; +use App\Filament\Resources\Manage\FeedDeliveries\FeedDeliveryResource; use Filament\Resources\Pages\ManageRecords; class ManageFeedDeliveries extends ManageRecords diff --git a/app/Filament/Resources/Manage/FeedSchedules/FeedScheduleResource.php b/app/Filament/Resources/Manage/FeedSchedules/FeedScheduleResource.php index d300922..879154c 100644 --- a/app/Filament/Resources/Manage/FeedSchedules/FeedScheduleResource.php +++ b/app/Filament/Resources/Manage/FeedSchedules/FeedScheduleResource.php @@ -33,13 +33,13 @@ class FeedScheduleResource extends Resource protected static string|UnitEnum|null $navigationGroup = 'Kelola'; - protected static string|BackedEnum|null $navigationIcon = Heroicon::Clock; + protected static string|BackedEnum|null $navigationIcon = Heroicon::BellAlert; protected static ?string $navigationLabel = 'Jadwal Pakan'; protected static ?int $navigationSort = 2; - protected static ?string $recordTitleAttribute = 'name'; + protected static ?string $recordTitleAttribute = 'scheduled_at'; protected static ?string $slug = 'manage/feed-schedules'; @@ -113,7 +113,7 @@ public static function table(Table $table): Table ...DefaultBulkActions::make('Jadwal Pakan'), ]), ]) - ->emptyStateIcon(Heroicon::Clock) + ->emptyStateIcon(Heroicon::BellAlert) ->emptyStateDescription('Setelah Anda membuat data pertama, maka akan muncul disini.') ->defaultSort('created_at', 'desc') ->deferFilters(false); diff --git a/app/Models/FeedDelivery.php b/app/Models/FeedDelivery.php index 4cc0df0..6ba36c4 100644 --- a/app/Models/FeedDelivery.php +++ b/app/Models/FeedDelivery.php @@ -5,10 +5,11 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; class FeedDelivery extends Model { - use HasFactory; + use HasFactory, SoftDeletes; protected $guarded = ['id']; @@ -19,11 +20,6 @@ protected function casts(): array ]; } - public function schedule(): BelongsTo - { - return $this->belongsTo(FeedSchedule::class); - } - public function feed(): BelongsTo { return $this->belongsTo(Feed::class); diff --git a/app/Models/FeedSchedule.php b/app/Models/FeedSchedule.php index bb194ba..0652a1e 100644 --- a/app/Models/FeedSchedule.php +++ b/app/Models/FeedSchedule.php @@ -5,7 +5,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; class FeedSchedule extends Model @@ -25,9 +24,4 @@ public function feed(): BelongsTo { return $this->belongsTo(Feed::class); } - - public function deliveries(): HasMany - { - return $this->hasMany(FeedDelivery::class); - } } diff --git a/database/migrations/2026_02_26_000002_create_feed_deliveries_table.php b/database/migrations/2026_02_26_000002_create_feed_deliveries_table.php index fffed78..15a4ed8 100644 --- a/database/migrations/2026_02_26_000002_create_feed_deliveries_table.php +++ b/database/migrations/2026_02_26_000002_create_feed_deliveries_table.php @@ -13,11 +13,12 @@ public function up(): void { Schema::create('feed_deliveries', function (Blueprint $table) { $table->id(); - $table->foreignId('feed_schedule_id')->nullable()->constrained()->cascadeOnDelete(); $table->foreignId('feed_id')->constrained()->cascadeOnDelete(); $table->unsignedInteger('quantity'); $table->timestamp('delivered_at')->useCurrent(); - $table->timestamps(); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); + $table->softDeletes(); }); } diff --git a/routes/console.php b/routes/console.php index 7741f6d..d17ffec 100644 --- a/routes/console.php +++ b/routes/console.php @@ -1,5 +1,16 @@ everyMinute(); +if (Schema::hasTable('feed_schedules')) { + $times = FeedSchedule::query() + ->distinct() + ->pluck('scheduled_at'); + + foreach ($times as $time) { + $formattedTime = date('H:i', strtotime($time)); + Schedule::command('feed:schedule-process', [$formattedTime])->at($formattedTime); + } +}