feat: add submittedBy relationship to Cutting model and enhance notification system for cutting updates and deletions

This commit is contained in:
Yoga Pangestu 2026-06-21 15:16:58 +07:00
parent 9170e5fd1e
commit 4cbd296c0c
9 changed files with 131 additions and 24 deletions

View File

@ -39,6 +39,11 @@ public function createdBy(): BelongsTo
return $this->belongsTo(User::class, 'created_by_id'); return $this->belongsTo(User::class, 'created_by_id');
} }
public function submittedBy(): BelongsTo
{
return $this->belongsTo(User::class, 'submitted_by_id');
}
public function materials(): HasMany public function materials(): HasMany
{ {
return $this->hasMany(CuttingMaterial::class); return $this->hasMany(CuttingMaterial::class);

View File

@ -106,11 +106,28 @@ public function update(EmployeeAdvance $employeeAdvance, array $validated, User
$employeeAdvance->due_date = $validated['due_date']; $employeeAdvance->due_date = $validated['due_date'];
$employeeAdvance->save(); $employeeAdvance->save();
}); });
$this->pushNotificationService->sendToRoles(
'✏️ Kasbon Diperbarui',
"Kasbon sebesar {$employeeAdvance->amount_formatted} telah diperbarui oleh {$user->profile?->full_name}.",
['owner', 'developer'],
'/admin/finance/employee-advances',
);
} }
public function delete(EmployeeAdvance $employeeAdvance, User $user): void public function delete(EmployeeAdvance $employeeAdvance, User $user): void
{ {
$amount = $employeeAdvance->amount_formatted;
$description = $employeeAdvance->description;
$employeeAdvance->delete(); $employeeAdvance->delete();
$this->pushNotificationService->sendToRoles(
'🗑️ Kasbon Dihapus',
"Kasbon sebesar {$amount} dengan keterangan {$description} telah dihapus.",
['owner', 'developer'],
'/admin/finance/employee-advances',
);
} }
public function approve(EmployeeAdvance $employeeAdvance, User $user): void public function approve(EmployeeAdvance $employeeAdvance, User $user): void

View File

@ -269,6 +269,15 @@ public function deleteAdjustment(PayrollAdjustment $adjustment): void
$payroll->recalculateAmounts(); $payroll->recalculateAmounts();
$payroll->save(); $payroll->save();
}); });
if ($payroll->employee?->user_id) {
$this->pushNotificationService->sendToUser(
'📊 Penyesuaian Gaji Dihapus',
"Penyesuaian gaji Anda untuk periode {$payroll->payrollPeriod->period_label} telah dihapus.",
$payroll->employee->user_id,
'/admin/finance/payrolls',
);
}
} }
public function pay(Payroll $payroll, User $user): void public function pay(Payroll $payroll, User $user): void

View File

@ -160,8 +160,19 @@ public function checkOut(array $validated, User $user): void
public function delete(Attendance $attendance): void public function delete(Attendance $attendance): void
{ {
$attendance->loadMissing('employee.user.profile');
$employeeName = $attendance->employee?->user?->profile?->full_name;
$date = $attendance->attendance_date;
$attendance->clearMediaCollection('checkin'); $attendance->clearMediaCollection('checkin');
$attendance->clearMediaCollection('checkout'); $attendance->clearMediaCollection('checkout');
$attendance->delete(); $attendance->delete();
$this->pushNotificationService->sendToRoles(
'🗑️ Presensi Dihapus',
"Data presensi {$employeeName} tanggal {$date} telah dihapus.",
['owner', 'developer'],
'/admin/hr/attendances',
);
} }
} }

View File

@ -101,11 +101,28 @@ public function update(LeaveRequest $leaveRequest, array $validated, User $user)
$leaveRequest->end_date = $endDate; $leaveRequest->end_date = $endDate;
$leaveRequest->total_days = $this->calculateTotalDays($startDate, $endDate); $leaveRequest->total_days = $this->calculateTotalDays($startDate, $endDate);
$leaveRequest->save(); $leaveRequest->save();
$this->pushNotificationService->sendToRoles(
'✏️ Pengajuan Cuti Diperbarui',
"Pengajuan cuti oleh {$user->profile?->full_name} telah diperbarui.",
['owner', 'developer'],
'/admin/hr/leave-requests',
);
} }
public function delete(LeaveRequest $leaveRequest): void public function delete(LeaveRequest $leaveRequest): void
{ {
$totalDays = $leaveRequest->total_days;
$employeeName = $leaveRequest->employee?->user?->profile?->full_name;
$leaveRequest->delete(); $leaveRequest->delete();
$this->pushNotificationService->sendToRoles(
'🗑️ Pengajuan Cuti Dihapus',
"Pengajuan cuti {$totalDays} hari oleh {$employeeName} telah dihapus.",
['owner', 'developer'],
'/admin/hr/leave-requests',
);
} }
public function approve(LeaveRequest $leaveRequest, User $user): void public function approve(LeaveRequest $leaveRequest, User $user): void

View File

@ -506,6 +506,14 @@ public function update(Cutting $cutting, array $validated): void
$this->deductMaterialStock($cutting); $this->deductMaterialStock($cutting);
} }
}); });
$description = $cutting->description ?? '-';
$this->pushNotificationService->sendToRoles(
'✏️ Proses Cutting Diperbarui',
"Proses cutting dengan deskripsi '{$description}' telah diperbarui.",
['owner', 'developer'],
'/admin/manage/cuttings',
);
} }
public function delete(Cutting $cutting): void public function delete(Cutting $cutting): void
@ -615,10 +623,14 @@ public function transitionStatus(
default => '✂️ Proses Cutting Diperbarui', default => '✂️ Proses Cutting Diperbarui',
}; };
$roles = $status === CuttingStatus::COMPLETED
? ['owner', 'developer', 'admin-toko']
: ['owner', 'developer'];
$this->pushNotificationService->sendToRoles( $this->pushNotificationService->sendToRoles(
$title, $title,
$message, $message,
['owner', 'developer'], $roles,
'/admin/manage/cuttings', '/admin/manage/cuttings',
); );
} }

View File

@ -109,6 +109,7 @@ public function submitVerification(
]); ]);
} }
$cutting->submitted_by_id = $user->id;
$cutting->status = CuttingStatus::PENDING_VERIFICATION; $cutting->status = CuttingStatus::PENDING_VERIFICATION;
$cutting->save(); $cutting->save();
}); });
@ -195,6 +196,15 @@ public function rejectVerification(
['owner', 'developer'], ['owner', 'developer'],
'/admin/manage/owner-verifications', '/admin/manage/owner-verifications',
); );
if ($cutting->submitted_by_id) {
$this->pushNotificationService->sendToUser(
'📦 Verifikasi Cutting Ditolak Owner',
"Cutting dengan deskripsi '{$description}' yang Anda verifikasi ditolak oleh owner dengan alasan: '{$reason}'.",
$cutting->submitted_by_id,
'/admin/manage/owner-verifications',
);
}
} }
private function applyProductStockOnVerify(Cutting $cutting): void private function applyProductStockOnVerify(Cutting $cutting): void

View File

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('cuttings', function (Blueprint $table) {
$table->foreignId('submitted_by_id')
->nullable()
->after('created_by_id')
->constrained('users')
->nullOnDelete();
});
}
public function down(): void
{
Schema::table('cuttings', function (Blueprint $table) {
$table->dropConstrainedForeignId('submitted_by_id');
});
}
};

View File

@ -15,30 +15,30 @@ public function run(): void
RolePermissionSeeder::class, RolePermissionSeeder::class,
UserSeeder::class, UserSeeder::class,
SystemConfigurationSeeder::class, SystemConfigurationSeeder::class,
CategorySeeder::class, // CategorySeeder::class,
ProductSeeder::class, // ProductSeeder::class,
ProductVariantSeeder::class, // ProductVariantSeeder::class,
RawMaterialSeeder::class, // RawMaterialSeeder::class,
RawMaterialPriceSeeder::class, // RawMaterialPriceSeeder::class,
SupplierSeeder::class, // SupplierSeeder::class,
CustomerSeeder::class, // CustomerSeeder::class,
CashAccountSeeder::class, CashAccountSeeder::class,
CashTransactionSeeder::class, // CashTransactionSeeder::class,
ExpenseSeeder::class, // ExpenseSeeder::class,
PayrollPeriodSeeder::class, // PayrollPeriodSeeder::class,
PayrollSeeder::class, // PayrollSeeder::class,
PayrollAdjustmentSeeder::class, // PayrollAdjustmentSeeder::class,
AttendanceSeeder::class, // AttendanceSeeder::class,
LeaveRequestSeeder::class, // LeaveRequestSeeder::class,
EmployeeAdvanceSeeder::class, // EmployeeAdvanceSeeder::class,
RejectionSeeder::class, // RejectionSeeder::class,
PurchaseSeeder::class, // PurchaseSeeder::class,
PurchaseItemSeeder::class, // PurchaseItemSeeder::class,
OrderSeeder::class, // OrderSeeder::class,
OrderItemSeeder::class, // OrderItemSeeder::class,
CuttingSeeder::class, // CuttingSeeder::class,
CuttingMaterialSeeder::class, // CuttingMaterialSeeder::class,
CuttingResultSeeder::class, // CuttingResultSeeder::class,
]); ]);
} }
} }