feat: Implement automated payroll closing with push notifications and refine payroll filtering and status management.

This commit is contained in:
Yoga Pangestu 2025-12-18 09:39:33 +07:00
parent 36004254c9
commit 40db1c006c
6 changed files with 36 additions and 5 deletions

View File

@ -3,7 +3,9 @@
namespace App\Console\Commands;
use App\Enums\IsPaid;
use App\Jobs\StorePushNotification;
use App\Models\Payroll;
use App\Models\PushNotification;
use Carbon\Carbon;
use Illuminate\Console\Command;
@ -23,14 +25,30 @@ public function handle(): void
$updatedCount = Payroll::where('period_month', $targetMonth)
->where('is_paid', IsPaid::NOT_PAID)
->update(['is_paid' => IsPaid::PAID]);
->update([
'is_paid' => IsPaid::PAID,
'paid_at' => now(),
]);
if ($updatedCount === 0) {
$this->warn("Tidak ada payroll yang perlu ditutup untuk bulan {$targetMonth}.");
$this->warn('Tidak ada payroll yang perlu ditutup untuk bulan '.formatDateLocalized($targetMonth, 'F Y').'.');
return;
}
$this->info("Payroll untuk bulan {$targetMonth} berhasil ditandai selesai. Total: {$updatedCount}");
$userIds = Payroll::where('period_month', $targetMonth)
->where('is_paid', IsPaid::PAID)
->pluck('user_id')
->unique();
StorePushNotification::dispatch(
PushNotification::whereIn('user_id', $userIds)->get(),
[
'title' => 'Gaji Sudah Cair 💸',
'body' => 'Kerja kerasmu terbayar. Gaji bulan '.formatDateLocalized($targetMonth, 'F Y').' sudah kami transfer. Semoga harimu menyenangkan.',
],
);
$this->info('Payroll untuk bulan '.formatDateLocalized($targetMonth, 'F Y')." berhasil ditandai selesai. Total: {$updatedCount}");
}
}

View File

@ -24,6 +24,7 @@ public function handle(): void
$users = User::whereHas('employee')
->active()
->withoutDeveloper()
->with('employee')
->get();

View File

@ -96,7 +96,7 @@ public function builder(): Builder
'payrolls.id',
'payrolls.user_id',
'period_month',
'base_salary',
'payrolls.base_salary',
'bonus',
'deduction',
'total_salary',
@ -104,7 +104,7 @@ public function builder(): Builder
'paid_at',
'payrolls.created_at'
)
->where('period_month', '!=', now()->format('Y-m'))
->paid()
->with(['user', 'user.employee']);
}
}

View File

@ -54,7 +54,12 @@ public function mount()
private function loadPayrolls()
{
$this->payrolls = PayrollModel::with(['user', 'user.employee'])
->notPaid()
->where('period_month', now()->format('Y-m'))
->whereHas('user', function ($query) {
$query->active()
->withoutDeveloper();
})
->get();
}

View File

@ -38,6 +38,12 @@ public function paid(Builder $query): void
$query->where('is_paid', IsPaid::PAID->value);
}
#[Scope]
public function notPaid(Builder $query): void
{
$query->where('is_paid', IsPaid::NOT_PAID->value);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);

View File

@ -3,4 +3,5 @@
use Illuminate\Support\Facades\Schedule;
Schedule::command('payroll:generate --current')->monthly();
Schedule::command('payroll:close --current')->lastDayOfMonth('23:59');
Schedule::command('stock-opname:generate')->monthlyOn(25, '00:00');