refactor: make employee employment fields nullable and update related salary calculations and display logic
This commit is contained in:
parent
bdeb8cc59f
commit
175cb4acab
@ -35,7 +35,13 @@ public function label(): string
|
||||
public function permissions(): array
|
||||
{
|
||||
return match ($this) {
|
||||
self::DEVELOPER, self::OWNER => Permission::cases(),
|
||||
self::DEVELOPER, self::OWNER => array_values(array_filter(
|
||||
Permission::cases(),
|
||||
fn (Permission $permission) => ! in_array($permission, [
|
||||
Permission::ATTENDANCES_CREATE,
|
||||
Permission::ATTENDANCES_DELETE,
|
||||
], true)
|
||||
)),
|
||||
|
||||
self::DIREKTUR => [
|
||||
Permission::DASHBOARD_VIEW,
|
||||
|
||||
@ -25,6 +25,8 @@ public function authorize(): bool
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$anyEmployee = ! empty($this->join_date) || ! empty($this->employment_status) || ! empty($this->base_salary);
|
||||
|
||||
return [
|
||||
'email' => ['required', 'email', 'max:100', Rule::unique('users', 'email')->ignore($this->route('user')?->id)],
|
||||
'username' => ['required', 'string', 'max:20', 'alpha_dash', Rule::unique('users', 'username')->ignore($this->route('user')?->id)],
|
||||
@ -33,9 +35,9 @@ public function rules(): array
|
||||
'gender' => ['nullable', Rule::enum(Gender::class)],
|
||||
'birth_date' => ['nullable', 'date', 'before:today'],
|
||||
'address' => ['nullable', 'string'],
|
||||
'join_date' => ['required', 'date'],
|
||||
'employment_status' => ['required', Rule::enum(EmploymentStatus::class)],
|
||||
'base_salary' => ['required', 'integer', 'min:0'],
|
||||
'join_date' => [$anyEmployee ? 'required' : 'nullable', 'date'],
|
||||
'employment_status' => [$anyEmployee ? 'required' : 'nullable', Rule::enum(EmploymentStatus::class)],
|
||||
'base_salary' => [$anyEmployee ? 'required' : 'nullable', 'integer', 'min:0'],
|
||||
'role' => ['required', Rule::in(Role::assignableValues())],
|
||||
];
|
||||
}
|
||||
|
||||
@ -86,21 +86,21 @@ public function user(): BelongsTo
|
||||
public function baseSalaryFormatted(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => 'Rp '.number_format($this->base_salary, 0, ',', '.'),
|
||||
get: fn () => $this->base_salary !== null ? 'Rp '.number_format($this->base_salary, 0, ',', '.') : '-',
|
||||
);
|
||||
}
|
||||
|
||||
public function employmentStatusLabel(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->employment_status?->label(),
|
||||
get: fn () => $this->employment_status?->label() ?? '-',
|
||||
);
|
||||
}
|
||||
|
||||
public function joinDateFormatted(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => Carbon::parse($this->join_date)->translatedFormat('l, d F Y'),
|
||||
get: fn () => $this->join_date ? Carbon::parse($this->join_date)->translatedFormat('l, d F Y') : '-',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ public function payrollPeriod(): BelongsTo
|
||||
public function baseSalaryFormatted(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => 'Rp '.number_format($this->base_salary, 0, ',', '.'),
|
||||
get: fn () => $this->base_salary !== null ? 'Rp '.number_format($this->base_salary, 0, ',', '.') : '-',
|
||||
);
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ public function calculateKasbonDeduction(): int
|
||||
->where('status', EmployeeAdvanceStatus::APPROVED)
|
||||
->sum('amount');
|
||||
|
||||
return min($outstanding, $this->base_salary + (int) $this->adjustments()
|
||||
return min($outstanding, (int) $this->base_salary + (int) $this->adjustments()
|
||||
->where('type', PayrollAdjustmentType::BONUS)
|
||||
->sum('amount'));
|
||||
}
|
||||
@ -152,6 +152,6 @@ public function recalculateAmounts(): void
|
||||
|
||||
$this->bonus_amount = $bonusAmount;
|
||||
$this->deduction_amount = $kasbonDeduction + $manualDeduction;
|
||||
$this->total_amount = max(0, $this->base_salary + $bonusAmount - $this->deduction_amount);
|
||||
$this->total_amount = max(0, (int) $this->base_salary + $bonusAmount - $this->deduction_amount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ public function generatePayrollsForPeriod(PayrollPeriod $period): void
|
||||
$payroll = new Payroll([
|
||||
'payroll_period_id' => $period->id,
|
||||
'employee_id' => $employee->id,
|
||||
'base_salary' => $employee->base_salary,
|
||||
'base_salary' => $employee->base_salary ?? 0,
|
||||
'bonus_amount' => 0,
|
||||
'deduction_amount' => 0,
|
||||
'total_amount' => 0,
|
||||
|
||||
@ -24,7 +24,6 @@ public function paginateForIndex(
|
||||
): LengthAwarePaginator {
|
||||
$query = User::query()
|
||||
->with(['profile', 'employee', 'roles'])
|
||||
->whereHas('employee')
|
||||
->when($tableQuery['search'] !== '', function ($query) use ($tableQuery): void {
|
||||
$search = $tableQuery['search'];
|
||||
$query->where(function ($query) use ($search): void {
|
||||
@ -81,12 +80,16 @@ public function create(array $validated): void
|
||||
'address' => $validated['address'],
|
||||
]);
|
||||
|
||||
Employee::create([
|
||||
'user_id' => $user->id,
|
||||
'join_date' => $validated['join_date'],
|
||||
'employment_status' => $validated['employment_status'],
|
||||
'base_salary' => $validated['base_salary'],
|
||||
]);
|
||||
$hasEmployee = !empty($validated['join_date']) && !empty($validated['employment_status']) && !empty($validated['base_salary']);
|
||||
|
||||
if ($hasEmployee) {
|
||||
Employee::create([
|
||||
'user_id' => $user->id,
|
||||
'join_date' => $validated['join_date'],
|
||||
'employment_status' => $validated['employment_status'],
|
||||
'base_salary' => $validated['base_salary'],
|
||||
]);
|
||||
}
|
||||
|
||||
$user->syncRoles([$validated['role']]);
|
||||
});
|
||||
@ -112,10 +115,27 @@ public function update(User $user, array $validated): void
|
||||
$profile->address = $validated['address'];
|
||||
$profile->save();
|
||||
|
||||
$employee->join_date = $validated['join_date'];
|
||||
$employee->employment_status = $validated['employment_status'];
|
||||
$employee->base_salary = $validated['base_salary'];
|
||||
$employee->save();
|
||||
$hasEmployee = !empty($validated['join_date']) && !empty($validated['employment_status']) && !empty($validated['base_salary']);
|
||||
|
||||
if ($hasEmployee) {
|
||||
if ($employee) {
|
||||
$employee->join_date = $validated['join_date'];
|
||||
$employee->employment_status = $validated['employment_status'];
|
||||
$employee->base_salary = $validated['base_salary'];
|
||||
$employee->save();
|
||||
} else {
|
||||
Employee::create([
|
||||
'user_id' => $user->id,
|
||||
'join_date' => $validated['join_date'],
|
||||
'employment_status' => $validated['employment_status'],
|
||||
'base_salary' => $validated['base_salary'],
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
if ($employee) {
|
||||
$employee->delete();
|
||||
}
|
||||
}
|
||||
|
||||
$user->syncRoles([$validated['role']]);
|
||||
});
|
||||
|
||||
@ -21,8 +21,8 @@ public function run(): void
|
||||
Payroll::factory()->create([
|
||||
'payroll_period_id' => $period->id,
|
||||
'employee_id' => $employee->id,
|
||||
'base_salary' => $employee->base_salary,
|
||||
'total_amount' => $employee->base_salary,
|
||||
'base_salary' => $employee->base_salary ?? 0,
|
||||
'total_amount' => $employee->base_salary ?? 0,
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
@ -56,12 +56,19 @@ const form = useForm<EmployeeFormData>({
|
||||
birth_date: props.initialData?.birth_date ?? '',
|
||||
address: props.initialData?.address ?? '',
|
||||
join_date: props.initialData?.join_date ?? '',
|
||||
employment_status: props.initialData?.employment_status ?? 'full_time',
|
||||
employment_status: props.initialData?.employment_status ?? '',
|
||||
base_salary: props.initialData?.base_salary ?? '',
|
||||
role: props.initialData?.role ?? '',
|
||||
});
|
||||
|
||||
function submit() {
|
||||
form.transform((data) => ({
|
||||
...data,
|
||||
join_date: data.join_date === '' ? null : data.join_date,
|
||||
employment_status: data.employment_status === 'none' || data.employment_status === '' ? null : data.employment_status,
|
||||
base_salary: data.base_salary === '' ? null : data.base_salary,
|
||||
}));
|
||||
|
||||
const options = {
|
||||
onError: () => {
|
||||
toast.error('Gagal menyimpan data. Periksa kembali formulir.');
|
||||
@ -173,23 +180,24 @@ function submit() {
|
||||
<FieldGroup>
|
||||
<FieldSet class="grid gap-4 md:grid-cols-3">
|
||||
<Field>
|
||||
<FieldLabel for="join_date" required>Tanggal Bergabung</FieldLabel>
|
||||
<FieldLabel for="join_date">Tanggal Bergabung</FieldLabel>
|
||||
<DatePicker id="join_date" v-model="form.join_date"
|
||||
placeholder="Pilih tanggal bergabung" />
|
||||
<FieldError :errors="formErrors(form, 'join_date')" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel for="base_salary" required>Gaji Pokok</FieldLabel>
|
||||
<FieldLabel for="base_salary">Gaji Pokok</FieldLabel>
|
||||
<RupiahInput id="base_salary" v-model="form.base_salary" />
|
||||
<FieldError :errors="formErrors(form, 'base_salary')" />
|
||||
</Field>
|
||||
<Field>
|
||||
<FieldLabel for="employment_status" required>Status Kepegawaian</FieldLabel>
|
||||
<FieldLabel for="employment_status">Status Kepegawaian</FieldLabel>
|
||||
<Select v-model="form.employment_status">
|
||||
<SelectTrigger id="employment_status" class="w-full">
|
||||
<SelectValue placeholder="Pilih status kepegawaian" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">Pilih status kepegawaian</SelectItem>
|
||||
<SelectItem v-for="option in employmentStatuses" :key="option.value"
|
||||
:value="option.value">
|
||||
{{ option.label }}
|
||||
|
||||
@ -23,7 +23,7 @@ const initialData = computed(() => ({
|
||||
birth_date: props.employee.profile?.birth_date_input ?? '',
|
||||
address: props.employee.profile?.address ?? '',
|
||||
join_date: props.employee.employee?.join_date_input ?? '',
|
||||
employment_status: props.employee.employee?.employment_status ?? 'full_time',
|
||||
employment_status: props.employee.employee?.employment_status ?? '',
|
||||
base_salary: props.employee.employee?.base_salary != null ? String(props.employee.employee.base_salary) : '',
|
||||
role: props.employee.roles?.[0]?.name ?? '',
|
||||
}));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user