feat: enhance notification system with new NotificationService methods and integrate notifications for various actions in services
This commit is contained in:
parent
f6503ace2b
commit
9ab2b0505c
@ -10,6 +10,7 @@
|
|||||||
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\NotificationService;
|
||||||
use App\Settings\HRSettings;
|
use App\Settings\HRSettings;
|
||||||
use Carbon\CarbonInterface;
|
use Carbon\CarbonInterface;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
@ -131,11 +132,21 @@ private function handleLate(
|
|||||||
'attendance_id' => $attendance->id,
|
'attendance_id' => $attendance->id,
|
||||||
'type' => PayrollAdjustmentType::DEDUCTION,
|
'type' => PayrollAdjustmentType::DEDUCTION,
|
||||||
'amount' => $hrSettings->late_penalty_amount,
|
'amount' => $hrSettings->late_penalty_amount,
|
||||||
'description' => 'Denda keterlambatan '.$date->format('d/m/Y').' - '.$lateMinutes.' menit',
|
'description' => 'Denda keterlambatan '.$date->translatedFormat('l, d F Y').' - '.$lateMinutes.' menit',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->recalculatePayroll($payroll);
|
$this->recalculatePayroll($payroll);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$employeeUser = $payroll->employee->user ?? null;
|
||||||
|
if ($employeeUser) {
|
||||||
|
NotificationService::notifyUsers(
|
||||||
|
[$employeeUser],
|
||||||
|
'Denda Keterlambatan',
|
||||||
|
'Anda terkena denda keterlambatan sebesar Rp '.number_format($hrSettings->late_penalty_amount, 0, ',', '.')." pada {$date->translatedFormat('l, d F Y')} ({$lateMinutes} menit).",
|
||||||
|
route('admin.finance.payroll-periods.index'),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleAbsent(
|
private function handleAbsent(
|
||||||
@ -149,7 +160,7 @@ private function handleAbsent(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$description = 'Denda ketidakhadiran '.$date->format('d/m/Y');
|
$description = 'Denda ketidakhadiran '.$date->translatedFormat('l, d F Y');
|
||||||
|
|
||||||
$existing = PayrollAdjustment::where('payroll_id', $payroll->id)
|
$existing = PayrollAdjustment::where('payroll_id', $payroll->id)
|
||||||
->where('type', PayrollAdjustmentType::DEDUCTION)
|
->where('type', PayrollAdjustmentType::DEDUCTION)
|
||||||
@ -172,6 +183,16 @@ private function handleAbsent(
|
|||||||
|
|
||||||
$this->recalculatePayroll($payroll);
|
$this->recalculatePayroll($payroll);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$employeeUser = $employee->user ?? null;
|
||||||
|
if ($employeeUser) {
|
||||||
|
NotificationService::notifyUsers(
|
||||||
|
[$employeeUser],
|
||||||
|
'Denda Ketidakhadiran',
|
||||||
|
'Anda terkena denda ketidakhadiran sebesar Rp '.number_format($hrSettings->absent_penalty_amount, 0, ',', '.')." pada {$date->translatedFormat('l, d F Y')}.",
|
||||||
|
route('admin.finance.payroll-periods.index'),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function recalculatePayroll(Payroll $payroll): void
|
private function recalculatePayroll(Payroll $payroll): void
|
||||||
|
|||||||
@ -6,13 +6,12 @@
|
|||||||
use App\Models\Attendance;
|
use App\Models\Attendance;
|
||||||
use App\Models\LeaveRequest;
|
use App\Models\LeaveRequest;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Notifications\WebPushNotification;
|
use App\Services\NotificationService;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
class SendAttendanceReminderJob implements ShouldQueue
|
class SendAttendanceReminderJob implements ShouldQueue
|
||||||
{
|
{
|
||||||
@ -25,7 +24,7 @@ public function handle(): void
|
|||||||
$today = now()->toDateString();
|
$today = now()->toDateString();
|
||||||
|
|
||||||
$users = User::query()
|
$users = User::query()
|
||||||
->select(['id', 'name'])
|
->select(['id'])
|
||||||
->active()
|
->active()
|
||||||
->whereHas('roles', function ($q) {
|
->whereHas('roles', function ($q) {
|
||||||
$q->whereHas('permissions', fn($pq) => $pq->where('name', Permission::ATTENDANCES_CREATE->value));
|
$q->whereHas('permissions', fn($pq) => $pq->where('name', Permission::ATTENDANCES_CREATE->value));
|
||||||
@ -33,11 +32,11 @@ public function handle(): void
|
|||||||
->whereHas('employee')
|
->whereHas('employee')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
foreach ($users as $user) {
|
$eligibleUsers = $users->filter(function (User $user) use ($today) {
|
||||||
$employee = $user->employee;
|
$employee = $user->employee;
|
||||||
|
|
||||||
if (! $employee) {
|
if (! $employee) {
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$hasAttendedToday = Attendance::where('employee_id', $employee->id)
|
$hasAttendedToday = Attendance::where('employee_id', $employee->id)
|
||||||
@ -45,7 +44,7 @@ public function handle(): void
|
|||||||
->exists();
|
->exists();
|
||||||
|
|
||||||
if ($hasAttendedToday) {
|
if ($hasAttendedToday) {
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$isOnLeave = LeaveRequest::approved()
|
$isOnLeave = LeaveRequest::approved()
|
||||||
@ -54,19 +53,18 @@ public function handle(): void
|
|||||||
->where('end_date', '>=', $today)
|
->where('end_date', '>=', $today)
|
||||||
->exists();
|
->exists();
|
||||||
|
|
||||||
if ($isOnLeave) {
|
return ! $isOnLeave;
|
||||||
continue;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if ($eligibleUsers->isEmpty()) {
|
||||||
$user->notify(new WebPushNotification(
|
return;
|
||||||
title: 'Reminder Presensi',
|
|
||||||
body: 'Selamat pagi! Jangan lupa untuk melakukan presensi masuk hari ini.',
|
|
||||||
url: route('admin.hr.attendances.index'),
|
|
||||||
));
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error("Gagal mengirim reminder presensi ke user {$user->id}: {$e->getMessage()}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NotificationService::notifyUsers(
|
||||||
|
$eligibleUsers,
|
||||||
|
'Reminder Presensi',
|
||||||
|
'Selamat pagi! Jangan lupa untuk melakukan presensi masuk hari ini.',
|
||||||
|
route('admin.hr.attendances.index'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -147,7 +147,7 @@ public function pay(Payroll $payroll): Payroll
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
title: 'Gaji Dibayar',
|
title: 'Gaji Dibayar',
|
||||||
body: "Gaji karyawan {$payroll->employee->name} sebesar {$payroll->formatted_amount} telah dibayar".' oleh '.auth()->user()->full_name.'.',
|
body: "Gaji {$employeeUser->full_name} sebesar {$payroll->formatted_amount} telah dibayar oleh " . auth()->user()->full_name . '.',
|
||||||
url: route('admin.finance.payroll-periods.index'),
|
url: route('admin.finance.payroll-periods.index'),
|
||||||
additionalUser: $employeeUser,
|
additionalUser: $employeeUser,
|
||||||
);
|
);
|
||||||
@ -178,7 +178,7 @@ public function cancel(Payroll $payroll): Payroll
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
title: 'Gaji Dibatalkan',
|
title: 'Gaji Dibatalkan',
|
||||||
body: "Gaji karyawan {$payroll->employee->name} sebesar {$payroll->formatted_amount} telah dibatalkan".' oleh '.auth()->user()->full_name.'.',
|
body: "Gaji {$employeeUser->full_name} sebesar {$payroll->formatted_amount} telah dibatalkan".' oleh '.auth()->user()->full_name.'.',
|
||||||
url: route('admin.finance.payroll-periods.index'),
|
url: route('admin.finance.payroll-periods.index'),
|
||||||
additionalUser: $employeeUser,
|
additionalUser: $employeeUser,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Services\Admin\Manage;
|
namespace App\Services\Admin\Manage;
|
||||||
|
|
||||||
|
use App\Enums\Role;
|
||||||
use App\Models\Cutting;
|
use App\Models\Cutting;
|
||||||
use App\Models\CuttingMaterial;
|
use App\Models\CuttingMaterial;
|
||||||
use App\Models\CuttingMaterialCombination;
|
use App\Models\CuttingMaterialCombination;
|
||||||
use App\Models\CuttingResult;
|
use App\Models\CuttingResult;
|
||||||
use App\Models\RawMaterialPrice;
|
use App\Models\RawMaterialPrice;
|
||||||
use App\Services\Concerns\RegistersMedia;
|
use App\Services\Concerns\RegistersMedia;
|
||||||
|
use App\Services\NotificationService;
|
||||||
use App\Services\S3PresignedService;
|
use App\Services\S3PresignedService;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -61,7 +63,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
|||||||
return $paginator;
|
return $paginator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMaterials(Cutting $cutting): \Illuminate\Support\Collection
|
public function getMaterials(Cutting $cutting): Collection
|
||||||
{
|
{
|
||||||
return $cutting->cuttingMaterials()
|
return $cutting->cuttingMaterials()
|
||||||
->select(['id', 'cutting_id', 'raw_material_price_id', 'material_usage', 'material_result', 'combination_id'])
|
->select(['id', 'cutting_id', 'raw_material_price_id', 'material_usage', 'material_result', 'combination_id'])
|
||||||
@ -85,7 +87,7 @@ public function getMaterials(Cutting $cutting): \Illuminate\Support\Collection
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCombinations(Cutting $cutting): \Illuminate\Support\Collection
|
public function getCombinations(Cutting $cutting): Collection
|
||||||
{
|
{
|
||||||
return $cutting->cuttingMaterialCombinations()
|
return $cutting->cuttingMaterialCombinations()
|
||||||
->select(['id', 'cutting_id', 'material_result'])
|
->select(['id', 'cutting_id', 'material_result'])
|
||||||
@ -248,7 +250,7 @@ public function getForEdit(Cutting $cutting): array
|
|||||||
|
|
||||||
public function store(array $data): Cutting
|
public function store(array $data): Cutting
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($data) {
|
$cutting = DB::transaction(function () use ($data) {
|
||||||
foreach ($data['materials'] as $materialData) {
|
foreach ($data['materials'] as $materialData) {
|
||||||
$usage = (int) ($materialData['material_usage'] ?? 0);
|
$usage = (int) ($materialData['material_usage'] ?? 0);
|
||||||
if ($usage <= 0) {
|
if ($usage <= 0) {
|
||||||
@ -346,11 +348,20 @@ public function store(array $data): Cutting
|
|||||||
|
|
||||||
return $cutting;
|
return $cutting;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_BAHAN_BAKU],
|
||||||
|
title: 'Cutting Baru',
|
||||||
|
body: 'Cutting berhasil ditambahkan oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.cuttings.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $cutting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Cutting $cutting, array $data): Cutting
|
public function update(Cutting $cutting, array $data): Cutting
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($cutting, $data) {
|
$cutting = DB::transaction(function () use ($cutting, $data) {
|
||||||
$cutting->load(['cuttingMaterials.rawMaterialPrice']);
|
$cutting->load(['cuttingMaterials.rawMaterialPrice']);
|
||||||
|
|
||||||
foreach ($cutting->cuttingMaterials as $oldMaterial) {
|
foreach ($cutting->cuttingMaterials as $oldMaterial) {
|
||||||
@ -450,11 +461,20 @@ public function update(Cutting $cutting, array $data): Cutting
|
|||||||
|
|
||||||
return $cutting;
|
return $cutting;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_BAHAN_BAKU],
|
||||||
|
title: 'Cutting Diperbarui',
|
||||||
|
body: 'Cutting berhasil diperbarui oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.cuttings.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $cutting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Cutting $cutting): bool
|
public function destroy(Cutting $cutting): bool
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($cutting) {
|
$result = DB::transaction(function () use ($cutting) {
|
||||||
$cutting->load('cuttingMaterials.rawMaterialPrice');
|
$cutting->load('cuttingMaterials.rawMaterialPrice');
|
||||||
|
|
||||||
foreach ($cutting->cuttingMaterials as $material) {
|
foreach ($cutting->cuttingMaterials as $material) {
|
||||||
@ -470,5 +490,14 @@ public function destroy(Cutting $cutting): bool
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_BAHAN_BAKU],
|
||||||
|
title: 'Cutting Dihapus',
|
||||||
|
body: 'Cutting berhasil dihapus oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.cuttings.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -340,7 +340,7 @@ private function storeNew(array $data): Purchase
|
|||||||
|
|
||||||
public function update(Purchase $purchase, array $data): Purchase
|
public function update(Purchase $purchase, array $data): Purchase
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($purchase, $data) {
|
$purchase = DB::transaction(function () use ($purchase, $data) {
|
||||||
$purchase->load('purchaseItems.rawMaterialPrice.rawMaterial');
|
$purchase->load('purchaseItems.rawMaterialPrice.rawMaterial');
|
||||||
|
|
||||||
$oldItems = $purchase->purchaseItems;
|
$oldItems = $purchase->purchaseItems;
|
||||||
@ -478,11 +478,20 @@ public function update(Purchase $purchase, array $data): Purchase
|
|||||||
|
|
||||||
return $purchase;
|
return $purchase;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_BAHAN_BAKU],
|
||||||
|
title: 'Belanja Diperbarui',
|
||||||
|
body: 'Belanja bahan baku sebesar '.$purchase->formatted_total.' berhasil diperbarui oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.purchases.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $purchase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Purchase $purchase): bool
|
public function destroy(Purchase $purchase): bool
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($purchase) {
|
$result = DB::transaction(function () use ($purchase) {
|
||||||
$purchase->load('purchaseItems.rawMaterialPrice');
|
$purchase->load('purchaseItems.rawMaterialPrice');
|
||||||
|
|
||||||
// Remove the stock the purchase added, keep the variants.
|
// Remove the stock the purchase added, keep the variants.
|
||||||
@ -497,5 +506,14 @@ public function destroy(Purchase $purchase): bool
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_BAHAN_BAKU],
|
||||||
|
title: 'Belanja Dihapus',
|
||||||
|
body: 'Belanja bahan baku berhasil dihapus oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.purchases.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -107,7 +107,7 @@ public function store(array $data): Restock
|
|||||||
|
|
||||||
public function update(Restock $restock, array $data): Restock
|
public function update(Restock $restock, array $data): Restock
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($restock, $data) {
|
$restock = DB::transaction(function () use ($restock, $data) {
|
||||||
$restock->load('restockItems');
|
$restock->load('restockItems');
|
||||||
|
|
||||||
$restock->restockItems->each(function (RestockItem $item) use ($restock) {
|
$restock->restockItems->each(function (RestockItem $item) use ($restock) {
|
||||||
@ -138,11 +138,20 @@ public function update(Restock $restock, array $data): Restock
|
|||||||
|
|
||||||
return $restock;
|
return $restock;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
||||||
|
title: 'Restock Diperbarui',
|
||||||
|
body: 'Restock berhasil diperbarui oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.restocks.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $restock;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Restock $restock): bool
|
public function destroy(Restock $restock): bool
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($restock) {
|
$result = DB::transaction(function () use ($restock) {
|
||||||
$restock->load('restockItems');
|
$restock->load('restockItems');
|
||||||
|
|
||||||
$restock->restockItems->each(function (RestockItem $item) use ($restock) {
|
$restock->restockItems->each(function (RestockItem $item) use ($restock) {
|
||||||
@ -154,6 +163,15 @@ public function destroy(Restock $restock): bool
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
||||||
|
title: 'Restock Dihapus',
|
||||||
|
body: 'Restock berhasil dihapus oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.restocks.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildItemRows(array $items, string $stockType, $now, int &$total): array
|
private function buildItemRows(array $items, string $stockType, $now, int &$total): array
|
||||||
|
|||||||
@ -94,6 +94,44 @@ public function paginated(int $perPage = 25, string $search = '', string $sort =
|
|||||||
return $paginator;
|
return $paginator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getForEdit(Order $order): array
|
||||||
|
{
|
||||||
|
$order->load([
|
||||||
|
'orderItems:id,order_id,product_variant_id,stock_quality,quantity,unit_price',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$stockType = $order->orderItems->first()?->stock_quality?->value ?? 'good';
|
||||||
|
|
||||||
|
$orderMedia = $order->getFirstMedia('photos');
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => $order->id,
|
||||||
|
'order_number' => $order->order_number,
|
||||||
|
'stock_type' => $stockType,
|
||||||
|
'channel' => $order->channel->value,
|
||||||
|
'price_type' => $order->price_type->value,
|
||||||
|
'payment_type' => $order->payment_type->value,
|
||||||
|
'customer_id' => $order->customer_id,
|
||||||
|
'marketing_id' => $order->marketing_id,
|
||||||
|
'discount' => $order->discount,
|
||||||
|
'nego_price' => $order->nego_price,
|
||||||
|
'is_completed' => $order->status === OrderStatus::COMPLETED,
|
||||||
|
'tiktok_order_id' => $order->tiktok_order_id,
|
||||||
|
'shopee_order_id' => $order->shopee_order_id,
|
||||||
|
'notes' => $order->notes,
|
||||||
|
'photo_key' => $orderMedia?->getCustomProperty('s3_key') ?? $orderMedia?->file_name,
|
||||||
|
'photo_url' => $orderMedia
|
||||||
|
? $this->s3Service->getTemporaryUrl($orderMedia->getPath())
|
||||||
|
: null,
|
||||||
|
'items' => $order->orderItems->map(fn (OrderItem $item) => [
|
||||||
|
'id' => $item->id,
|
||||||
|
'product_variant_id' => $item->product_variant_id,
|
||||||
|
'quantity' => $item->quantity,
|
||||||
|
'unit_price' => $item->unit_price,
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function getItems(Order $order): \Illuminate\Support\Collection
|
public function getItems(Order $order): \Illuminate\Support\Collection
|
||||||
{
|
{
|
||||||
return $order->orderItems()
|
return $order->orderItems()
|
||||||
@ -224,7 +262,7 @@ public function store(array $data): Order
|
|||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
||||||
title: 'Transaksi Baru',
|
title: 'Transaksi Baru',
|
||||||
body: 'Transaksi '.$order->order_number.' sebesar Rp '.number_format($totalAmount, 0, ',', '.').' berhasil dicatat oleh '.auth()->user()->full_name.'.',
|
body: 'Transaksi '.$order->order_number.' sebesar Rp '.$order->formatted_total_amount.' berhasil dicatat oleh '.auth()->user()->full_name.'.',
|
||||||
url: route('admin.manage.transactions.index'),
|
url: route('admin.manage.transactions.index'),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -234,7 +272,7 @@ public function store(array $data): Order
|
|||||||
|
|
||||||
public function update(Order $order, array $data): Order
|
public function update(Order $order, array $data): Order
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($order, $data) {
|
$order = DB::transaction(function () use ($order, $data) {
|
||||||
$order->load('orderItems');
|
$order->load('orderItems');
|
||||||
|
|
||||||
$oldStockType = $order->orderItems->first()?->stock_type?->value ?? ProductStockQuality::GOOD->value;
|
$oldStockType = $order->orderItems->first()?->stock_type?->value ?? ProductStockQuality::GOOD->value;
|
||||||
@ -296,11 +334,20 @@ public function update(Order $order, array $data): Order
|
|||||||
|
|
||||||
return $order;
|
return $order;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
||||||
|
title: 'Transaksi Diperbarui',
|
||||||
|
body: 'Transaksi '.$order->order_number.' sebesar '.$order->formatted_total_amount.' berhasil diperbarui oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.transactions.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $order;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Order $order): bool
|
public function destroy(Order $order): bool
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($order) {
|
$result = DB::transaction(function () use ($order) {
|
||||||
$order->load('orderItems');
|
$order->load('orderItems');
|
||||||
|
|
||||||
$stockType = $order->orderItems->first()?->stock_type?->value ?? ProductStockQuality::GOOD->value;
|
$stockType = $order->orderItems->first()?->stock_type?->value ?? ProductStockQuality::GOOD->value;
|
||||||
@ -314,6 +361,15 @@ public function destroy(Order $order): bool
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
||||||
|
title: 'Transaksi Dihapus',
|
||||||
|
body: 'Transaksi '.$order->order_number.' berhasil dihapus oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.manage.transactions.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateStatus(Order $order, string $status): Order
|
public function updateStatus(Order $order, string $status): Order
|
||||||
|
|||||||
@ -439,6 +439,15 @@ public function toggleStatus(Product $product): void
|
|||||||
$product->update([
|
$product->update([
|
||||||
'status' => $product->status->value === 'active' ? 'inactive' : 'active',
|
'status' => $product->status->value === 'active' ? 'inactive' : 'active',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$status = $product->status->value === 'active' ? 'diaktifkan' : 'dinonaktifkan';
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
|
title: 'Status Produk Diubah',
|
||||||
|
body: "Produk \"{$product->name}\" berhasil {$status}".' oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.master.products.index'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toggleFeatured(Product $product): void
|
public function toggleFeatured(Product $product): void
|
||||||
@ -448,6 +457,15 @@ public function toggleFeatured(Product $product): void
|
|||||||
$product->update([
|
$product->update([
|
||||||
'is_featured' => ! $product->is_featured,
|
'is_featured' => ! $product->is_featured,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$status = $product->is_featured ? 'ditandai sebagai unggulan' : 'dihapus dari unggulan';
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
|
title: 'Status Unggulan Produk Diubah',
|
||||||
|
body: "Produk \"{$product->name}\" berhasil {$status}".' oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.master.products.index'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function approve(Product $product): void
|
public function approve(Product $product): void
|
||||||
@ -457,7 +475,7 @@ public function approve(Product $product): void
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
||||||
title: 'Produk Disetujui',
|
title: 'Produk Disetujui',
|
||||||
body: "Produk \"{$product->name}\" telah disetujui oleh ".auth()->user()->full_name.'.',
|
body: "Produk \"{$product->name}\" telah disetujui oleh ".auth()->user()->full_name.'.',
|
||||||
url: route('admin.master.products.index'),
|
url: route('admin.master.products.index'),
|
||||||
@ -473,7 +491,7 @@ public function reject(Product $product, string $reason = ''): void
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
||||||
title: 'Produk Ditolak',
|
title: 'Produk Ditolak',
|
||||||
body: "Produk \"{$product->name}\" telah ditolak oleh ".auth()->user()->full_name.'.',
|
body: "Produk \"{$product->name}\" telah ditolak oleh ".auth()->user()->full_name.'.',
|
||||||
url: route('admin.master.products.index'),
|
url: route('admin.master.products.index'),
|
||||||
@ -489,7 +507,7 @@ public function resubmit(Product $product): void
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
NotificationService::notify(
|
NotificationService::notify(
|
||||||
roles: [Role::OWNER, Role::DEVELOPER],
|
roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
|
||||||
title: 'Produk Diajukan Ulang',
|
title: 'Produk Diajukan Ulang',
|
||||||
body: "Produk \"{$product->name}\" telah diajukan ulang oleh ".auth()->user()->full_name.'.',
|
body: "Produk \"{$product->name}\" telah diajukan ulang oleh ".auth()->user()->full_name.'.',
|
||||||
url: route('admin.master.products.index'),
|
url: route('admin.master.products.index'),
|
||||||
|
|||||||
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Services\Admin\Master\RawMaterial;
|
namespace App\Services\Admin\Master\RawMaterial;
|
||||||
|
|
||||||
|
use App\Enums\Role;
|
||||||
use App\Models\RawMaterial;
|
use App\Models\RawMaterial;
|
||||||
use App\Models\RawMaterialPrice;
|
use App\Models\RawMaterialPrice;
|
||||||
use App\Services\Concerns\RegistersMedia;
|
use App\Services\Concerns\RegistersMedia;
|
||||||
|
use App\Services\NotificationService;
|
||||||
use App\Services\S3PresignedService;
|
use App\Services\S3PresignedService;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -71,7 +73,7 @@ public function getVariants(RawMaterial $rawMaterial): Collection
|
|||||||
|
|
||||||
public function store(array $data): RawMaterial
|
public function store(array $data): RawMaterial
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($data) {
|
$rawMaterial = DB::transaction(function () use ($data) {
|
||||||
$rawMaterial = RawMaterial::create([
|
$rawMaterial = RawMaterial::create([
|
||||||
'name' => $data['name'],
|
'name' => $data['name'],
|
||||||
'unit' => $data['unit'],
|
'unit' => $data['unit'],
|
||||||
@ -103,6 +105,15 @@ public function store(array $data): RawMaterial
|
|||||||
|
|
||||||
return $rawMaterial;
|
return $rawMaterial;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
|
title: 'Bahan Baku Baru',
|
||||||
|
body: "Bahan baku \"{$rawMaterial->name}\" berhasil ditambahkan".' oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.master.raw-materials.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $rawMaterial;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getForEdit(RawMaterial $rawMaterial): array
|
public function getForEdit(RawMaterial $rawMaterial): array
|
||||||
@ -137,7 +148,7 @@ public function getForEdit(RawMaterial $rawMaterial): array
|
|||||||
|
|
||||||
public function update(RawMaterial $rawMaterial, array $data): RawMaterial
|
public function update(RawMaterial $rawMaterial, array $data): RawMaterial
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($rawMaterial, $data) {
|
$rawMaterial = DB::transaction(function () use ($rawMaterial, $data) {
|
||||||
$rawMaterial->update([
|
$rawMaterial->update([
|
||||||
'name' => $data['name'],
|
'name' => $data['name'],
|
||||||
'unit' => $data['unit'],
|
'unit' => $data['unit'],
|
||||||
@ -214,17 +225,35 @@ public function update(RawMaterial $rawMaterial, array $data): RawMaterial
|
|||||||
|
|
||||||
return $rawMaterial;
|
return $rawMaterial;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
|
title: 'Bahan Baku Diperbarui',
|
||||||
|
body: "Bahan baku \"{$rawMaterial->name}\" berhasil diperbarui".' oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.master.raw-materials.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $rawMaterial;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(RawMaterial $rawMaterial): bool
|
public function destroy(RawMaterial $rawMaterial): bool
|
||||||
{
|
{
|
||||||
return DB::transaction(function () use ($rawMaterial) {
|
$result = DB::transaction(function () use ($rawMaterial) {
|
||||||
$rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) {
|
$rawMaterial->rawMaterialPrices->each(function (RawMaterialPrice $price) {
|
||||||
$price->delete();
|
$price->delete();
|
||||||
});
|
});
|
||||||
|
|
||||||
return $rawMaterial->delete();
|
return $rawMaterial->delete();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
|
title: 'Bahan Baku Dihapus',
|
||||||
|
body: "Bahan baku \"{$rawMaterial->name}\" berhasil dihapus".' oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.master.raw-materials.index'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toggleStatus(RawMaterial $rawMaterial): void
|
public function toggleStatus(RawMaterial $rawMaterial): void
|
||||||
@ -232,5 +261,14 @@ public function toggleStatus(RawMaterial $rawMaterial): void
|
|||||||
$rawMaterial->update([
|
$rawMaterial->update([
|
||||||
'is_active' => ! $rawMaterial->is_active,
|
'is_active' => ! $rawMaterial->is_active,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$status = $rawMaterial->is_active ? 'diaktifkan' : 'dinonaktifkan';
|
||||||
|
|
||||||
|
NotificationService::notify(
|
||||||
|
roles: [Role::OWNER, Role::DEVELOPER, Role::DIREKTUR, Role::ADMIN_TOKO],
|
||||||
|
title: 'Status Bahan Baku Diubah',
|
||||||
|
body: "Bahan baku \"{$rawMaterial->name}\" berhasil {$status}".' oleh '.auth()->user()->full_name.'.',
|
||||||
|
url: route('admin.master.raw-materials.index'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
use App\Enums\Role;
|
use App\Enums\Role;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Notifications\WebPushNotification;
|
use App\Notifications\WebPushNotification;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class NotificationService
|
class NotificationService
|
||||||
@ -12,32 +13,52 @@ class NotificationService
|
|||||||
/**
|
/**
|
||||||
* @param array<Role> $roles
|
* @param array<Role> $roles
|
||||||
*/
|
*/
|
||||||
public static function notify(array $roles, string $title, string $body, string $url, ?User $additionalUser = null): void
|
public static function notify(array $roles, string $title, string $body, string $url, ?User $additionalUser = null, ?User $except = null): void
|
||||||
{
|
{
|
||||||
$roleLabels = array_map(fn (Role $role) => $role->label(), $roles);
|
$except ??= auth()->user();
|
||||||
|
|
||||||
|
$roleValues = array_map(fn (Role $role) => $role->value, $roles);
|
||||||
|
|
||||||
$users = User::query()
|
$users = User::query()
|
||||||
->select(['id'])
|
->select(['id'])
|
||||||
->where('is_active', true)
|
->where('is_active', true)
|
||||||
->whereHas('roles', fn ($q) => $q->whereIn('name', $roleLabels))
|
->where('id', '!=', $except?->id)
|
||||||
|
->whereHas('roles', fn ($q) => $q->whereIn('name', $roleValues))
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
if ($additionalUser && ! $users->contains('id', $additionalUser->id)) {
|
if ($additionalUser && $additionalUser->id !== $except?->id && ! $users->contains('id', $additionalUser->id)) {
|
||||||
$users->push($additionalUser);
|
$users->push($additionalUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
$users->each(function (User $user) use ($title, $body, $url) {
|
$users->each(function (User $user) use ($title, $body, $url) {
|
||||||
try {
|
self::sendNotification($user, $title, $body, $url);
|
||||||
$user->notifications()->create([
|
|
||||||
'title' => $title,
|
|
||||||
'body' => $body,
|
|
||||||
'url' => $url,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$user->notify(new WebPushNotification($title, $body));
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
Log::error("Gagal mengirim notifikasi ke user {$user->id}: {$e->getMessage()}");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Collection<int, User>|array<int, User> $users
|
||||||
|
*/
|
||||||
|
public static function notifyUsers(Collection|array $users, string $title, string $body, string $url): void
|
||||||
|
{
|
||||||
|
$users = $users instanceof Collection ? $users : collect($users);
|
||||||
|
|
||||||
|
$users->each(function (User $user) use ($title, $body, $url) {
|
||||||
|
self::sendNotification($user, $title, $body, $url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function sendNotification(User $user, string $title, string $body, string $url): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$user->notifications()->create([
|
||||||
|
'title' => $title,
|
||||||
|
'body' => $body,
|
||||||
|
'url' => $url,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user->notify(new WebPushNotification($title, $body));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error("Gagal mengirim notifikasi ke user {$user->id}: {$e->getMessage()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user