feat: enhance ApplyAttendancePenalties command with transaction management and system user tracking; refactor attendance penalty logic for improved clarity and efficiency; update PayrollService to utilize transaction handling for adjustments; schedule attendance penalties application in app bootstrap
This commit is contained in:
parent
37d9ac10a6
commit
8404b8af41
@ -3,14 +3,18 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Enums\PayrollAdjustmentType;
|
use App\Enums\PayrollAdjustmentType;
|
||||||
|
use App\Enums\Role;
|
||||||
use App\Models\Attendance;
|
use App\Models\Attendance;
|
||||||
use App\Models\Employee;
|
use App\Models\Employee;
|
||||||
use App\Models\LeaveRequest;
|
use App\Models\LeaveRequest;
|
||||||
use App\Models\Payroll;
|
use App\Models\Payroll;
|
||||||
use App\Models\PayrollPeriod;
|
use App\Models\PayrollPeriod;
|
||||||
|
use App\Models\User;
|
||||||
use App\Settings\HrSettings;
|
use App\Settings\HrSettings;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class ApplyAttendancePenalties extends Command
|
class ApplyAttendancePenalties extends Command
|
||||||
{
|
{
|
||||||
@ -36,11 +40,16 @@ public function handle(): int
|
|||||||
}
|
}
|
||||||
|
|
||||||
$scheduledCheckIn = Carbon::createFromFormat('Y-m-d H:i', $date->format('Y-m-d').' '.$scheduledTime);
|
$scheduledCheckIn = Carbon::createFromFormat('Y-m-d H:i', $date->format('Y-m-d').' '.$scheduledTime);
|
||||||
|
$createdBy = $this->resolveSystemUser();
|
||||||
|
|
||||||
|
if ($createdBy === null) {
|
||||||
|
$this->error('Tidak ditemukan user sistem (developer/owner) untuk mencatat penalti.');
|
||||||
|
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
$employees = Employee::query()
|
$employees = Employee::query()
|
||||||
->whereHas('user', function ($query) {
|
->whereHas('user', fn ($query) => $query->active())
|
||||||
$query->active();
|
|
||||||
})
|
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$lateCount = 0;
|
$lateCount = 0;
|
||||||
@ -48,106 +57,118 @@ public function handle(): int
|
|||||||
$skippedLeave = 0;
|
$skippedLeave = 0;
|
||||||
$skippedDuplicate = 0;
|
$skippedDuplicate = 0;
|
||||||
|
|
||||||
foreach ($employees as $employee) {
|
try {
|
||||||
$hasLeave = LeaveRequest::query()
|
DB::transaction(function () use ($employees, $date, $scheduledCheckIn, $latePenalty, $absentPenalty, $createdBy, &$lateCount, &$absentCount, &$skippedLeave, &$skippedDuplicate): void {
|
||||||
->approved()
|
foreach ($employees as $employee) {
|
||||||
->where('employee_id', $employee->id)
|
$hasLeave = LeaveRequest::query()
|
||||||
->whereDate('start_date', '<=', $date)
|
->approved()
|
||||||
->whereDate('end_date', '>=', $date)
|
->where('employee_id', $employee->id)
|
||||||
->exists();
|
->whereDate('start_date', '<=', $date)
|
||||||
|
->whereDate('end_date', '>=', $date)
|
||||||
|
->exists();
|
||||||
|
|
||||||
if ($hasLeave) {
|
if ($hasLeave) {
|
||||||
$skippedLeave++;
|
$skippedLeave++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$attendance = Attendance::query()
|
$attendance = Attendance::query()
|
||||||
->where('employee_id', $employee->id)
|
->where('employee_id', $employee->id)
|
||||||
->whereDate('attendance_date', $date)
|
->whereDate('attendance_date', $date)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if ($attendance === null) {
|
if ($attendance === null) {
|
||||||
if ($absentPenalty <= 0) {
|
if ($absentPenalty <= 0) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payroll = $this->findOpenPayroll($employee->id);
|
||||||
|
|
||||||
|
if ($payroll === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$alreadyExists = $payroll->adjustments()
|
||||||
|
->whereNull('attendance_id')
|
||||||
|
->deduction()
|
||||||
|
->where('description', 'like', "%Bolos {$date->format('d/m/Y')}%")
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($alreadyExists) {
|
||||||
|
$skippedDuplicate++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payroll->adjustments()->create([
|
||||||
|
'type' => PayrollAdjustmentType::DEDUCTION,
|
||||||
|
'amount' => $absentPenalty,
|
||||||
|
'description' => "Bolos {$date->format('d/m/Y')}",
|
||||||
|
'created_by_id' => $createdBy->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$payroll->load('adjustments');
|
||||||
|
$payroll->recalculateAmounts();
|
||||||
|
$payroll->save();
|
||||||
|
|
||||||
|
$absentCount++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($latePenalty <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$checkInTime = Carbon::parse($attendance->check_in_at);
|
||||||
|
|
||||||
|
if ($checkInTime->lte($scheduledCheckIn)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$payroll = $this->findOpenPayroll($employee->id);
|
||||||
|
|
||||||
|
if ($payroll === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$alreadyExists = $payroll->adjustments()
|
||||||
|
->where('attendance_id', $attendance->id)
|
||||||
|
->where('type', PayrollAdjustmentType::DEDUCTION)
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($alreadyExists) {
|
||||||
|
$skippedDuplicate++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$minutesLate = (int) $scheduledCheckIn->diffInMinutes($checkInTime);
|
||||||
|
|
||||||
|
$payroll->adjustments()->create([
|
||||||
|
'type' => PayrollAdjustmentType::DEDUCTION,
|
||||||
|
'amount' => $latePenalty,
|
||||||
|
'description' => "Terlambat {$date->format('d/m/Y')} ({$minutesLate} menit)",
|
||||||
|
'attendance_id' => $attendance->id,
|
||||||
|
'created_by_id' => $createdBy->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$payroll->load('adjustments');
|
||||||
|
$payroll->recalculateAmounts();
|
||||||
|
$payroll->save();
|
||||||
|
|
||||||
|
$lateCount++;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
$payroll = $this->findOpenPayroll($employee->id);
|
} catch (\Throwable $e) {
|
||||||
|
Log::error("Gagal menerapkan penalti presensi: {$e->getMessage()}", [
|
||||||
if ($payroll === null) {
|
'trace' => $e->getTraceAsString(),
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$alreadyExists = $payroll->adjustments()
|
|
||||||
->where('attendance_id', null)
|
|
||||||
->deduction()
|
|
||||||
->where('description', 'like', "%Bolos {$date->format('d/m/Y')}%")
|
|
||||||
->exists();
|
|
||||||
|
|
||||||
if ($alreadyExists) {
|
|
||||||
$skippedDuplicate++;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$payroll->adjustments()->create([
|
|
||||||
'type' => PayrollAdjustmentType::DEDUCTION,
|
|
||||||
'amount' => $absentPenalty,
|
|
||||||
'description' => "Bolos {$date->format('d/m/Y')}",
|
|
||||||
'created_by_id' => 1,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$payroll->load('adjustments');
|
|
||||||
$payroll->recalculateAmounts();
|
|
||||||
$payroll->save();
|
|
||||||
|
|
||||||
$absentCount++;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($latePenalty <= 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$checkInTime = Carbon::parse($attendance->check_in_at);
|
|
||||||
|
|
||||||
if ($checkInTime->lte($scheduledCheckIn)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$payroll = $this->findOpenPayroll($employee->id);
|
|
||||||
|
|
||||||
if ($payroll === null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$alreadyExists = $payroll->adjustments()
|
|
||||||
->where('attendance_id', $attendance->id)
|
|
||||||
->where('type', PayrollAdjustmentType::DEDUCTION)
|
|
||||||
->exists();
|
|
||||||
|
|
||||||
if ($alreadyExists) {
|
|
||||||
$skippedDuplicate++;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$minutesLate = (int) $scheduledCheckIn->diffInMinutes($checkInTime);
|
|
||||||
|
|
||||||
$payroll->adjustments()->create([
|
|
||||||
'type' => PayrollAdjustmentType::DEDUCTION,
|
|
||||||
'amount' => $latePenalty,
|
|
||||||
'description' => "Terlambat {$date->format('d/m/Y')} ({$minutesLate} menit)",
|
|
||||||
'attendance_id' => $attendance->id,
|
|
||||||
'created_by_id' => 1,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$payroll->load('adjustments');
|
$this->error('Gagal menerapkan penalti presensi: '.$e->getMessage());
|
||||||
$payroll->recalculateAmounts();
|
|
||||||
$payroll->save();
|
|
||||||
|
|
||||||
$lateCount++;
|
return self::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->info("Selesai memproses tanggal {$date->format('d/m/Y')}:");
|
$this->info("Selesai memproses tanggal {$date->format('d/m/Y')}:");
|
||||||
@ -161,9 +182,7 @@ public function handle(): int
|
|||||||
|
|
||||||
private function findOpenPayroll(int $employeeId): ?Payroll
|
private function findOpenPayroll(int $employeeId): ?Payroll
|
||||||
{
|
{
|
||||||
$period = PayrollPeriod::query()
|
$period = PayrollPeriod::query()->open()->first();
|
||||||
->open()
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if ($period === null) {
|
if ($period === null) {
|
||||||
return null;
|
return null;
|
||||||
@ -174,4 +193,12 @@ private function findOpenPayroll(int $employeeId): ?Payroll
|
|||||||
->where('employee_id', $employeeId)
|
->where('employee_id', $employeeId)
|
||||||
->first();
|
->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolveSystemUser(): ?User
|
||||||
|
{
|
||||||
|
return User::query()
|
||||||
|
->whereHas('roles', fn ($query) => $query->whereIn('name', [Role::DEVELOPER->value, Role::OWNER->value]))
|
||||||
|
->first()
|
||||||
|
?? User::query()->first();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Enums\EmployeeAdvanceStatus;
|
||||||
use App\Enums\PayrollAdjustmentType;
|
use App\Enums\PayrollAdjustmentType;
|
||||||
use App\Enums\PayrollStatus;
|
use App\Enums\PayrollStatus;
|
||||||
use App\Models\Concerns\InteractsWithActivityLog;
|
use App\Models\Concerns\InteractsWithActivityLog;
|
||||||
|
|||||||
@ -13,17 +13,17 @@
|
|||||||
use App\Models\PayrollAdjustment;
|
use App\Models\PayrollAdjustment;
|
||||||
use App\Models\PayrollPeriod;
|
use App\Models\PayrollPeriod;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Services\Concerns\RunsInTransaction;
|
||||||
use App\Services\System\PushNotificationService;
|
use App\Services\System\PushNotificationService;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Validation\ValidationException;
|
|
||||||
|
|
||||||
class PayrollService
|
class PayrollService
|
||||||
{
|
{
|
||||||
|
use RunsInTransaction;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly CashService $cashService,
|
private readonly CashService $cashService,
|
||||||
private readonly PushNotificationService $pushNotificationService,
|
private readonly PushNotificationService $pushNotificationService,
|
||||||
@ -108,8 +108,8 @@ public function openCurrentPeriod(?User $closedBy = null): PayrollPeriod
|
|||||||
?? User::query()->whereHas('roles', fn (Builder $roleQuery) => $roleQuery->whereIn('name', [Role::DEVELOPER->value, Role::OWNER->value]))->first()
|
?? User::query()->whereHas('roles', fn (Builder $roleQuery) => $roleQuery->whereIn('name', [Role::DEVELOPER->value, Role::OWNER->value]))->first()
|
||||||
?? User::query()->first();
|
?? User::query()->first();
|
||||||
|
|
||||||
try {
|
$period = $this->runInTransaction(
|
||||||
$period = DB::transaction(function () use ($user): PayrollPeriod {
|
function () use ($user): PayrollPeriod {
|
||||||
$now = now();
|
$now = now();
|
||||||
$year = $now->year;
|
$year = $now->year;
|
||||||
$month = $now->month;
|
$month = $now->month;
|
||||||
@ -157,18 +157,9 @@ public function openCurrentPeriod(?User $closedBy = null): PayrollPeriod
|
|||||||
$this->generatePayrollsForPeriod($period);
|
$this->generatePayrollsForPeriod($period);
|
||||||
|
|
||||||
return $period->fresh();
|
return $period->fresh();
|
||||||
});
|
},
|
||||||
} catch (ValidationException $e) {
|
'Gagal membuka periode payroll',
|
||||||
throw $e;
|
);
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Gagal membuka periode payroll: '.$e->getMessage(), [
|
|
||||||
'trace' => $e->getTraceAsString(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $period;
|
return $period;
|
||||||
}
|
}
|
||||||
@ -205,9 +196,8 @@ public function generatePayrollsForPeriod(PayrollPeriod $period): void
|
|||||||
|
|
||||||
public function addAdjustment(Payroll $payroll, array $validated, User $user): void
|
public function addAdjustment(Payroll $payroll, array $validated, User $user): void
|
||||||
{
|
{
|
||||||
|
$this->runInTransaction(
|
||||||
try {
|
function () use ($payroll, $validated, $user): void {
|
||||||
DB::transaction(function () use ($payroll, $validated, $user): void {
|
|
||||||
$payroll->adjustments()->create([
|
$payroll->adjustments()->create([
|
||||||
'type' => PayrollAdjustmentType::from($validated['type']),
|
'type' => PayrollAdjustmentType::from($validated['type']),
|
||||||
'amount' => (int) $validated['amount'],
|
'amount' => (int) $validated['amount'],
|
||||||
@ -218,18 +208,9 @@ public function addAdjustment(Payroll $payroll, array $validated, User $user): v
|
|||||||
$payroll->load('adjustments');
|
$payroll->load('adjustments');
|
||||||
$payroll->recalculateAmounts();
|
$payroll->recalculateAmounts();
|
||||||
$payroll->save();
|
$payroll->save();
|
||||||
});
|
},
|
||||||
} catch (ValidationException $e) {
|
'Gagal menambahkan penyesuaian gaji',
|
||||||
throw $e;
|
);
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Gagal menambahkan penyesuaian gaji: '.$e->getMessage(), [
|
|
||||||
'trace' => $e->getTraceAsString(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($payroll->employee?->user_id) {
|
if ($payroll->employee?->user_id) {
|
||||||
$typeLabel = PayrollAdjustmentType::from($validated['type'])->label();
|
$typeLabel = PayrollAdjustmentType::from($validated['type'])->label();
|
||||||
@ -248,8 +229,8 @@ public function updateAdjustment(PayrollAdjustment $adjustment, array $validated
|
|||||||
$payroll = $adjustment->payroll;
|
$payroll = $adjustment->payroll;
|
||||||
$payroll->loadMissing(['payrollPeriod', 'employee.user']);
|
$payroll->loadMissing(['payrollPeriod', 'employee.user']);
|
||||||
|
|
||||||
try {
|
$this->runInTransaction(
|
||||||
DB::transaction(function () use ($payroll, $adjustment, $validated): void {
|
function () use ($payroll, $adjustment, $validated): void {
|
||||||
$adjustment->type = PayrollAdjustmentType::from($validated['type']);
|
$adjustment->type = PayrollAdjustmentType::from($validated['type']);
|
||||||
$adjustment->amount = (int) $validated['amount'];
|
$adjustment->amount = (int) $validated['amount'];
|
||||||
$adjustment->description = $validated['description'];
|
$adjustment->description = $validated['description'];
|
||||||
@ -258,18 +239,9 @@ public function updateAdjustment(PayrollAdjustment $adjustment, array $validated
|
|||||||
$payroll->load('adjustments');
|
$payroll->load('adjustments');
|
||||||
$payroll->recalculateAmounts();
|
$payroll->recalculateAmounts();
|
||||||
$payroll->save();
|
$payroll->save();
|
||||||
});
|
},
|
||||||
} catch (ValidationException $e) {
|
'Gagal memperbarui penyesuaian gaji',
|
||||||
throw $e;
|
);
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Gagal memperbarui penyesuaian gaji: '.$e->getMessage(), [
|
|
||||||
'trace' => $e->getTraceAsString(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($payroll->employee?->user_id) {
|
if ($payroll->employee?->user_id) {
|
||||||
$typeLabel = PayrollAdjustmentType::from($validated['type'])->label();
|
$typeLabel = PayrollAdjustmentType::from($validated['type'])->label();
|
||||||
@ -288,25 +260,16 @@ public function deleteAdjustment(PayrollAdjustment $adjustment): void
|
|||||||
$payroll = $adjustment->payroll;
|
$payroll = $adjustment->payroll;
|
||||||
$payroll->loadMissing(['payrollPeriod', 'employee.user']);
|
$payroll->loadMissing(['payrollPeriod', 'employee.user']);
|
||||||
|
|
||||||
try {
|
$this->runInTransaction(
|
||||||
DB::transaction(function () use ($payroll, $adjustment): void {
|
function () use ($payroll, $adjustment): void {
|
||||||
$adjustment->delete();
|
$adjustment->delete();
|
||||||
|
|
||||||
$payroll->load('adjustments');
|
$payroll->load('adjustments');
|
||||||
$payroll->recalculateAmounts();
|
$payroll->recalculateAmounts();
|
||||||
$payroll->save();
|
$payroll->save();
|
||||||
});
|
},
|
||||||
} catch (ValidationException $e) {
|
'Gagal menghapus penyesuaian gaji',
|
||||||
throw $e;
|
);
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Gagal menghapus penyesuaian gaji: '.$e->getMessage(), [
|
|
||||||
'trace' => $e->getTraceAsString(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($payroll->employee?->user_id) {
|
if ($payroll->employee?->user_id) {
|
||||||
$this->pushNotificationService->sendToUser(
|
$this->pushNotificationService->sendToUser(
|
||||||
@ -323,32 +286,23 @@ public function pay(Payroll $payroll, User $user): void
|
|||||||
$payroll->loadMissing(['payrollPeriod', 'employee.user.profile']);
|
$payroll->loadMissing(['payrollPeriod', 'employee.user.profile']);
|
||||||
|
|
||||||
if ($payroll->total_amount <= 0) {
|
if ($payroll->total_amount <= 0) {
|
||||||
try {
|
$this->runInTransaction(
|
||||||
DB::transaction(function () use ($payroll, $user): void {
|
function () use ($payroll, $user): void {
|
||||||
$payroll->status = PayrollStatus::PAID;
|
$payroll->status = PayrollStatus::PAID;
|
||||||
$payroll->paid_at = now();
|
$payroll->paid_at = now();
|
||||||
$payroll->paid_by_id = $user->id;
|
$payroll->paid_by_id = $user->id;
|
||||||
$payroll->save();
|
$payroll->save();
|
||||||
|
|
||||||
$this->settleKasbonFromPayroll($payroll, $user);
|
$this->settleKasbonFromPayroll($payroll, $user);
|
||||||
});
|
},
|
||||||
} catch (ValidationException $e) {
|
'Gagal membayar gaji (total 0)',
|
||||||
throw $e;
|
);
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Gagal membayar gaji (total 0): '.$e->getMessage(), [
|
|
||||||
'trace' => $e->getTraceAsString(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
$this->runInTransaction(
|
||||||
DB::transaction(function () use ($payroll, $user): void {
|
function () use ($payroll, $user): void {
|
||||||
$description = sprintf(
|
$description = sprintf(
|
||||||
'Pembayaran gaji: %s (%s)',
|
'Pembayaran gaji: %s (%s)',
|
||||||
$payroll->employeeName,
|
$payroll->employeeName,
|
||||||
@ -369,18 +323,9 @@ public function pay(Payroll $payroll, User $user): void
|
|||||||
$payroll->save();
|
$payroll->save();
|
||||||
|
|
||||||
$this->settleKasbonFromPayroll($payroll, $user);
|
$this->settleKasbonFromPayroll($payroll, $user);
|
||||||
});
|
},
|
||||||
} catch (ValidationException $e) {
|
'Gagal membayar gaji',
|
||||||
throw $e;
|
);
|
||||||
} catch (\Throwable $e) {
|
|
||||||
Log::error('Gagal membayar gaji: '.$e->getMessage(), [
|
|
||||||
'trace' => $e->getTraceAsString(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
|
||||||
'system' => 'Terjadi kesalahan pada server. Silakan laporkan masalah ini ke pihak terkait.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($payroll->employee?->user_id) {
|
if ($payroll->employee?->user_id) {
|
||||||
$this->pushNotificationService->sendToUser(
|
$this->pushNotificationService->sendToUser(
|
||||||
|
|||||||
@ -43,4 +43,8 @@
|
|||||||
$schedule->command('payroll:open-period')
|
$schedule->command('payroll:open-period')
|
||||||
->monthlyOn(1, '00:05')
|
->monthlyOn(1, '00:05')
|
||||||
->timezone('Asia/Jakarta');
|
->timezone('Asia/Jakarta');
|
||||||
|
|
||||||
|
$schedule->command('attendance:apply-penalties')
|
||||||
|
->dailyAt('23:55')
|
||||||
|
->timezone('Asia/Jakarta');
|
||||||
})->create();
|
})->create();
|
||||||
|
|||||||
@ -7,12 +7,16 @@ defineProps<{
|
|||||||
tooltip?: string;
|
tooltip?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
click: [];
|
||||||
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger as-child>
|
<TooltipTrigger as-child>
|
||||||
<Button variant="ghost" size="icon" class="size-8" :disabled="disabled">
|
<Button variant="ghost" size="icon" class="size-8" :disabled="disabled" @click="emit('click')">
|
||||||
<SlidersHorizontal class="size-4" />
|
<SlidersHorizontal class="size-4" />
|
||||||
<span class="sr-only">{{ tooltip || 'Sesuaikan' }}</span>
|
<span class="sr-only">{{ tooltip || 'Sesuaikan' }}</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user