298 lines
12 KiB
PHP
298 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Services\System;
|
|
|
|
use App\Enums\OrderChannel;
|
|
use App\Enums\OrderStatus;
|
|
use App\Enums\PaymentType;
|
|
use App\Enums\Permission;
|
|
use App\Enums\PriceType;
|
|
use App\Enums\Role;
|
|
use App\Models\Attendance;
|
|
use App\Models\CashAccount;
|
|
use App\Models\CashTransaction;
|
|
use App\Models\Employee;
|
|
use App\Models\EmployeeAdvance;
|
|
use App\Models\Expense;
|
|
use App\Models\LeaveRequest;
|
|
use App\Models\Order;
|
|
use App\Models\OrderItem;
|
|
use App\Models\Purchase;
|
|
use App\Models\User;
|
|
use Carbon\Carbon;
|
|
|
|
class DashboardService
|
|
{
|
|
public function getTodayAttendanceForUser(User $user): ?array
|
|
{
|
|
$employee = $user->employee;
|
|
|
|
if ($employee === null) {
|
|
return null;
|
|
}
|
|
|
|
$attendance = Attendance::query()
|
|
->with('media')
|
|
->where('employee_id', $employee->id)
|
|
->whereDate('attendance_date', today())
|
|
->first();
|
|
|
|
return $attendance?->toArray();
|
|
}
|
|
|
|
public function isOnLeaveTodayForUser(User $user): bool
|
|
{
|
|
$employee = $user->employee;
|
|
|
|
if ($employee === null) {
|
|
return false;
|
|
}
|
|
|
|
return LeaveRequest::query()
|
|
->approved()
|
|
->where('employee_id', $employee->id)
|
|
->whereDate('start_date', '<=', today())
|
|
->whereDate('end_date', '>=', today())
|
|
->exists();
|
|
}
|
|
|
|
public function canCheckInForUser(User $user): bool
|
|
{
|
|
return $user->can('attendances.create') && $user->employee !== null;
|
|
}
|
|
|
|
public function getAttendance(): array
|
|
{
|
|
/** @var User|null $user */
|
|
$user = auth()->user();
|
|
$isSuper = $user?->hasAnyRole(['developer', 'owner', 'direktur']) ?? false;
|
|
|
|
$attendanceRoles = collect(Role::cases())
|
|
->filter(fn (Role $role) => in_array(Permission::ATTENDANCES_VIEW, $role->permissions()))
|
|
->map(fn (Role $role) => $role->value)
|
|
->values()
|
|
->toArray();
|
|
|
|
$employeeQuery = Employee::query();
|
|
$attendanceQuery = Attendance::query()->where('attendance_date', Carbon::today());
|
|
$leaveRequestQuery = LeaveRequest::query()
|
|
->approved()
|
|
->where('start_date', '<=', Carbon::today())
|
|
->where('end_date', '>=', Carbon::today());
|
|
|
|
if (! $isSuper) {
|
|
$employeeId = $user?->employee?->id;
|
|
$employeeQuery->where('id', $employeeId);
|
|
$attendanceQuery->where('employee_id', $employeeId);
|
|
$leaveRequestQuery->where('employee_id', $employeeId);
|
|
}
|
|
|
|
$employeeQuery->whereHas('user.roles', fn ($q) => $q->whereIn('name', $attendanceRoles));
|
|
$totalEmployees = $employeeQuery->count();
|
|
$present = $attendanceQuery->distinct('employee_id')->count('employee_id');
|
|
$onLeave = $leaveRequestQuery->distinct('employee_id')->count('employee_id');
|
|
$absent = max(0, $totalEmployees - $present - $onLeave);
|
|
|
|
return [
|
|
'total_employees' => $totalEmployees,
|
|
'percentage' => $totalEmployees > 0 ? round($present / $totalEmployees * 100) : 0,
|
|
'present' => $present,
|
|
'absent' => $absent,
|
|
'on_leave' => $onLeave,
|
|
];
|
|
}
|
|
|
|
public function getCashOverview(): array
|
|
{
|
|
/** @var User|null $user */
|
|
$user = auth()->user();
|
|
$isSuper = $user?->hasAnyRole(['developer', 'owner', 'direktur']) ?? false;
|
|
|
|
$totalBalanceQuery = CashAccount::query();
|
|
$transactionQuery = CashTransaction::query()->whereDate('created_at', Carbon::today());
|
|
|
|
if (! $isSuper) {
|
|
$transactionQuery->where('created_by_id', $user->id);
|
|
$totalBalanceQuery->whereHas('transactions', fn ($q) => $q->where('created_by_id', $user->id));
|
|
}
|
|
|
|
$totalBalance = $totalBalanceQuery->sum('balance');
|
|
$summary = $transactionQuery
|
|
->selectRaw("
|
|
COUNT(*) as total_transactions,
|
|
COALESCE(SUM(CASE WHEN type = 'deposit' THEN amount ELSE 0 END), 0) as total_deposit,
|
|
COALESCE(SUM(CASE WHEN type = 'withdrawal' THEN amount ELSE 0 END), 0) as total_withdrawal
|
|
")
|
|
->first();
|
|
|
|
return [
|
|
'total_balance' => (int) $totalBalance,
|
|
'total_transactions' => (int) ($summary->total_transactions ?? 0),
|
|
'total_deposit' => (int) ($summary->total_deposit ?? 0),
|
|
'total_withdrawal' => (int) ($summary->total_withdrawal ?? 0),
|
|
];
|
|
}
|
|
|
|
public function getRevenueSummary(): array
|
|
{
|
|
/** @var User|null $user */
|
|
$user = auth()->user();
|
|
$isSuper = $user?->hasAnyRole(['developer', 'owner', 'direktur']) ?? false;
|
|
$isMarketing = $user?->hasAnyRole(['marketing-offline', 'marketing-online']) ?? false;
|
|
|
|
$revenueSummary = Order::query()
|
|
->completed()
|
|
->when(! $isSuper && $isMarketing, fn ($q) => $q->where('marketing_id', $user->id))
|
|
->when(! $isSuper && ! $isMarketing, fn ($q) => $q->where('created_by_id', $user->id))
|
|
->whereDate('created_at', Carbon::today())
|
|
->selectRaw('SUM(total_amount) as total_revenue, SUM(discount) as total_discount, COUNT(*) as total_orders, AVG(total_amount) as avg_order')
|
|
->first();
|
|
|
|
$totalMarketplaceFees = Order::query()
|
|
->completed()
|
|
->when(! $isSuper && $isMarketing, fn ($q) => $q->where('marketing_id', $user->id))
|
|
->when(! $isSuper && ! $isMarketing, fn ($q) => $q->where('created_by_id', $user->id))
|
|
->whereNotNull('marketplace_settings_snapshot')
|
|
->whereDate('created_at', Carbon::today())
|
|
->get()
|
|
->sum(fn ($order) => (int) ($order->marketplace_settings_snapshot['total_fee_amount'] ?? 0));
|
|
|
|
$totalCostPrice = OrderItem::query()
|
|
->join('orders', 'order_items.order_id', '=', 'orders.id')
|
|
->leftJoin('product_prices', function ($join) {
|
|
$join->on('order_items.product_variant_id', '=', 'product_prices.variant_id')
|
|
->where('product_prices.type', PriceType::HARGA_MODAL->value);
|
|
})
|
|
->where('orders.status', OrderStatus::COMPLETED)
|
|
->whereDate('orders.created_at', Carbon::today())
|
|
->when(! $isSuper && $isMarketing, fn ($q) => $q->where('orders.marketing_id', $user->id))
|
|
->when(! $isSuper && ! $isMarketing, fn ($q) => $q->where('orders.created_by_id', $user->id))
|
|
->selectRaw('COALESCE(SUM(order_items.quantity * product_prices.price), 0) as total_hpp')
|
|
->value('total_hpp');
|
|
|
|
return [
|
|
'total_revenue' => (int) ($revenueSummary->total_revenue ?? 0),
|
|
'total_discount' => (int) ($revenueSummary->total_discount ?? 0),
|
|
'total_marketplace_fees' => $totalMarketplaceFees,
|
|
'total_cost_price' => (int) ($totalCostPrice ?? 0),
|
|
'total_deduction' => (int) ($revenueSummary->total_discount ?? 0) + $totalMarketplaceFees + (int) ($totalCostPrice ?? 0),
|
|
'total_orders' => (int) ($revenueSummary->total_orders ?? 0),
|
|
'avg_order' => (int) ($revenueSummary->avg_order ?? 0),
|
|
];
|
|
}
|
|
|
|
public function getExpenseSummary(): array
|
|
{
|
|
$today = Carbon::today();
|
|
/** @var User|null $user */
|
|
$user = auth()->user();
|
|
$isSuper = $user?->hasAnyRole(['developer', 'owner', 'direktur']) ?? false;
|
|
|
|
$purchase = Purchase::query()
|
|
->whereDate('created_at', $today)
|
|
->when(! $isSuper, fn ($q) => $q->where('created_by_id', $user->id))
|
|
->selectRaw('COALESCE(SUM(total), 0) as total')
|
|
->first();
|
|
|
|
$expenses = Expense::query()
|
|
->whereDate('created_at', $today)
|
|
->when(! $isSuper, fn ($q) => $q->where('created_by_id', $user->id))
|
|
->selectRaw('COALESCE(SUM(amount), 0) as total')
|
|
->first();
|
|
|
|
$employeeAdvance = EmployeeAdvance::query()
|
|
->whereDate('created_at', $today)
|
|
->when(! $isSuper, fn ($q) => $q->where('employee_id', $user->employee?->id))
|
|
->selectRaw('COALESCE(SUM(amount), 0) as total')
|
|
->first();
|
|
|
|
$purchaseTotal = (int) ($purchase->total ?? 0);
|
|
if ($user?->hasRole(Role::ADMIN_TOKO->value)) {
|
|
$purchaseTotal = 0;
|
|
}
|
|
$expenseTotal = (int) ($expenses->total ?? 0);
|
|
$advanceTotal = (int) ($employeeAdvance->total ?? 0);
|
|
|
|
return [
|
|
'total' => $purchaseTotal + $expenseTotal + $advanceTotal,
|
|
'purchase_total' => $purchaseTotal,
|
|
'expense_total' => $expenseTotal,
|
|
'advance_total' => $advanceTotal,
|
|
];
|
|
}
|
|
|
|
public function getOrderStats(): array
|
|
{
|
|
/** @var User|null $user */
|
|
$user = auth()->user();
|
|
$isSuper = $user?->hasAnyRole(['developer', 'owner', 'direktur']) ?? false;
|
|
$isMarketing = $user?->hasAnyRole(['marketing-offline', 'marketing-online']) ?? false;
|
|
|
|
$byChannel = Order::query()
|
|
->when(! $isSuper && $isMarketing, fn ($q) => $q->where('marketing_id', $user->id))
|
|
->when(! $isSuper && ! $isMarketing, fn ($q) => $q->where('created_by_id', $user->id))
|
|
->whereDate('created_at', Carbon::today())
|
|
->selectRaw('channel, COUNT(*) as count, SUM(total_amount) as total')
|
|
->groupBy('channel')
|
|
->get()
|
|
->map(fn ($item) => [
|
|
'channel' => $item->channel instanceof OrderChannel ? $item->channel->value : $item->channel,
|
|
'label' => $item->channel instanceof OrderChannel ? $item->channel->label() : OrderChannel::from($item->channel)->label(),
|
|
'count' => (int) $item->count,
|
|
'total' => (int) $item->total,
|
|
]);
|
|
|
|
$byPaymentType = Order::query()
|
|
->when(! $isSuper && $isMarketing, fn ($q) => $q->where('marketing_id', $user->id))
|
|
->when(! $isSuper && ! $isMarketing, fn ($q) => $q->where('created_by_id', $user->id))
|
|
->whereDate('created_at', Carbon::today())
|
|
->selectRaw('payment_type, COUNT(*) as count, SUM(total_amount) as total')
|
|
->groupBy('payment_type')
|
|
->get()
|
|
->map(fn ($item) => [
|
|
'payment_type' => $item->payment_type instanceof PaymentType ? $item->payment_type->value : $item->payment_type,
|
|
'label' => $item->payment_type instanceof PaymentType ? $item->payment_type->label() : PaymentType::from($item->payment_type)->label(),
|
|
'count' => (int) $item->count,
|
|
'total' => (int) $item->total,
|
|
]);
|
|
|
|
$byMarketing = Order::query()
|
|
->when(! $isSuper && $isMarketing, fn ($q) => $q->where('marketing_id', $user->id))
|
|
->when(! $isSuper && ! $isMarketing, fn ($q) => $q->where('created_by_id', $user->id))
|
|
->whereNotNull('marketing_id')
|
|
->whereDate('orders.created_at', Carbon::today())
|
|
->join('users', 'orders.marketing_id', '=', 'users.id')
|
|
->leftJoin('user_profiles', 'users.id', '=', 'user_profiles.user_id')
|
|
->selectRaw('COALESCE(user_profiles.full_name, users.username) as name, COUNT(*) as count, SUM(orders.total_amount) as total')
|
|
->groupBy('orders.marketing_id', 'users.username', 'user_profiles.full_name')
|
|
->orderByDesc('total')
|
|
->limit(5)
|
|
->get()
|
|
->map(fn ($item) => [
|
|
'name' => $item->name,
|
|
'count' => (int) $item->count,
|
|
'total' => (int) $item->total,
|
|
]);
|
|
|
|
$byStatus = Order::query()
|
|
->when(! $isSuper && $isMarketing, fn ($q) => $q->where('marketing_id', $user->id))
|
|
->when(! $isSuper && ! $isMarketing, fn ($q) => $q->where('created_by_id', $user->id))
|
|
->whereDate('created_at', Carbon::today())
|
|
->selectRaw('status, COUNT(*) as count')
|
|
->groupBy('status')
|
|
->get()
|
|
->map(fn ($item) => [
|
|
'status' => $item->status instanceof OrderStatus ? $item->status->value : $item->status,
|
|
'label' => $item->status instanceof OrderStatus ? $item->status->label() : OrderStatus::from($item->status)->label(),
|
|
'count' => (int) $item->count,
|
|
]);
|
|
|
|
return [
|
|
'by_channel' => $byChannel->toArray(),
|
|
'by_payment_type' => $byPaymentType->toArray(),
|
|
'by_marketing' => $byMarketing->toArray(),
|
|
'by_status' => $byStatus->toArray(),
|
|
];
|
|
}
|
|
}
|