199 lines
6.6 KiB
PHP
199 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Enums\OrderChannel;
|
|
use App\Enums\OrderStatus;
|
|
use App\Enums\PaymentType;
|
|
use App\Models\Attendance;
|
|
use App\Models\Employee;
|
|
use App\Models\EmployeeAdvance;
|
|
use App\Models\Expense;
|
|
use App\Models\LeaveRequest;
|
|
use App\Models\Order;
|
|
use App\Models\User;
|
|
use Carbon\Carbon;
|
|
|
|
class DashboardService
|
|
{
|
|
public function getAttendanceStats(): array
|
|
{
|
|
$today = Carbon::now()->toDateString();
|
|
|
|
$totalEmployees = Employee::whereHas('user', fn ($q) => $q->where('is_active', true))->count();
|
|
|
|
$present = Attendance::where('attendance_date', $today)->count();
|
|
|
|
$onLeave = LeaveRequest::approved()
|
|
->where('start_date', '<=', $today)
|
|
->where('end_date', '>=', $today)
|
|
->count();
|
|
|
|
$absent = max(0, $totalEmployees - $present - $onLeave);
|
|
|
|
return [
|
|
'total_employees' => $totalEmployees,
|
|
'present' => $present,
|
|
'absent' => $absent,
|
|
'on_leave' => $onLeave,
|
|
'percentage' => $totalEmployees > 0 ? round(($present / $totalEmployees) * 100) : 0,
|
|
];
|
|
}
|
|
|
|
public function getRevenueSummary(): array
|
|
{
|
|
$today = Carbon::now()->toDateString();
|
|
|
|
$stats = Order::where('status', OrderStatus::COMPLETED)
|
|
->whereDate('created_at', $today)
|
|
->selectRaw('COUNT(*) as total_orders')
|
|
->selectRaw('COALESCE(SUM(total_amount), 0) as total_revenue')
|
|
->selectRaw('COALESCE(SUM(discount), 0) as total_discount')
|
|
->first();
|
|
|
|
return [
|
|
'total_revenue' => (int) $stats->total_revenue,
|
|
'total_discount' => (int) $stats->total_discount,
|
|
'total_marketplace_fees' => 0,
|
|
'total_deduction' => (int) $stats->total_discount,
|
|
'total_orders' => (int) $stats->total_orders,
|
|
'avg_order' => $stats->total_orders > 0 ? (int) ($stats->total_revenue / $stats->total_orders) : 0,
|
|
];
|
|
}
|
|
|
|
public function getExpenseSummary(): array
|
|
{
|
|
$today = Carbon::now()->toDateString();
|
|
|
|
$expenses = Expense::whereDate('created_at', $today)
|
|
->selectRaw('COALESCE(SUM(amount), 0) as total')
|
|
->first();
|
|
|
|
$cashAdvanceTotal = EmployeeAdvance::where('status', 'paid')
|
|
->whereDate('created_at', $today)
|
|
->selectRaw('COALESCE(SUM(amount), 0) as total')
|
|
->first();
|
|
|
|
$expenseTotal = Expense::whereDate('created_at', $today)
|
|
->selectRaw('COALESCE(SUM(amount), 0) as total')
|
|
->first();
|
|
|
|
return [
|
|
'total' => (int) $expenses->total,
|
|
'purchase_total' => 0,
|
|
'expense_total' => (int) $expenseTotal->total,
|
|
'advance_total' => (int) ($cashAdvanceTotal->total ?? 0),
|
|
];
|
|
}
|
|
|
|
public function getOrderStats(): array
|
|
{
|
|
$today = Carbon::now()->toDateString();
|
|
$baseQuery = Order::where('status', OrderStatus::COMPLETED)
|
|
->whereDate('created_at', $today);
|
|
|
|
$byChannel = collect(OrderChannel::values())->map(function ($channel) use ($baseQuery) {
|
|
$count = (clone $baseQuery)->where('channel', $channel)->count();
|
|
$label = OrderChannel::from($channel)->label();
|
|
|
|
return [
|
|
'channel' => $channel,
|
|
'label' => $label,
|
|
'count' => $count,
|
|
'total' => (int) (clone $baseQuery)->where('channel', $channel)->sum('total_amount'),
|
|
];
|
|
});
|
|
|
|
$byPaymentType = collect(PaymentType::values())->map(function ($paymentType) use ($baseQuery) {
|
|
$count = (clone $baseQuery)->where('payment_type', $paymentType)->count();
|
|
$label = PaymentType::from($paymentType)->label();
|
|
|
|
return [
|
|
'payment_type' => $paymentType,
|
|
'label' => $label,
|
|
'count' => $count,
|
|
'total' => (int) (clone $baseQuery)->where('payment_type', $paymentType)->sum('total_amount'),
|
|
];
|
|
});
|
|
|
|
$byMarketing = Order::where('status', OrderStatus::COMPLETED)
|
|
->whereDate('created_at', $today)
|
|
->whereNotNull('marketing_id')
|
|
->select('marketing_id')
|
|
->selectRaw('COUNT(*) as count')
|
|
->selectRaw('COALESCE(SUM(total_amount), 0) as total')
|
|
->groupBy('marketing_id')
|
|
->with('marketing:id')
|
|
->get()
|
|
->map(fn ($item) => [
|
|
'name' => $item->marketing?->userProfile->full_name ?? '-',
|
|
'count' => $item->count,
|
|
'total' => (int) $item->total,
|
|
]);
|
|
|
|
$byStatus = collect(OrderStatus::values())->map(function ($status) use ($baseQuery) {
|
|
$count = (clone $baseQuery)->where('status', $status)->count();
|
|
$label = OrderStatus::from($status)->label();
|
|
|
|
return [
|
|
'status' => $status,
|
|
'label' => $label,
|
|
'count' => $count,
|
|
];
|
|
});
|
|
|
|
return [
|
|
'by_channel' => $byChannel,
|
|
'by_payment_type' => $byPaymentType,
|
|
'by_marketing' => $byMarketing,
|
|
'by_status' => $byStatus,
|
|
];
|
|
}
|
|
|
|
public function getTodayAttendance(User $user): ?array
|
|
{
|
|
$employee = $user->employee;
|
|
|
|
if (! $employee) {
|
|
return null;
|
|
}
|
|
|
|
$attendance = Attendance::where('employee_id', $employee->id)
|
|
->where('attendance_date', now()->toDateString())
|
|
->first();
|
|
|
|
if (! $attendance) {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'id' => $attendance->id,
|
|
'attendance_date' => $attendance->attendance_date,
|
|
'check_in_at' => $attendance->check_in_at,
|
|
'check_out_at' => $attendance->check_out_at,
|
|
'check_in_photo' => null,
|
|
'check_out_photo' => null,
|
|
'check_in_latitude' => $attendance->check_in_latitude,
|
|
'check_in_longitude' => $attendance->check_in_longitude,
|
|
'check_out_latitude' => $attendance->check_out_latitude,
|
|
'check_out_longitude' => $attendance->check_out_longitude,
|
|
'work_duration_minutes' => $attendance->work_duration_minutes,
|
|
];
|
|
}
|
|
|
|
public function isOnLeave(User $user): bool
|
|
{
|
|
$employee = $user->employee;
|
|
|
|
if (! $employee) {
|
|
return false;
|
|
}
|
|
|
|
return LeaveRequest::approved()
|
|
->where('employee_id', $employee->id)
|
|
->where('start_date', '<=', now()->toDateString())
|
|
->where('end_date', '>=', now()->toDateString())
|
|
->exists();
|
|
}
|
|
}
|