ultimatepos/app/Services/User/UserProfileDataService.php

436 lines
16 KiB
PHP

<?php
namespace App\Services\User;
use App\BusinessLocation;
use App\Category;
use App\EmploymentAssignment;
use App\Services\Holding\HoldingHrmSyncService;
use App\User;
use App\Utils\ModuleUtil;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Spatie\Activitylog\Models\Activity;
class UserProfileDataService
{
public function __construct(
private ModuleUtil $moduleUtil,
private HoldingHrmSyncService $holdingHrmSyncService
) {
}
public function build(User $user, array $options = []): array
{
$viewer = $options['viewer'] ?? auth()->user();
$businessId = (int) $user->business_id;
$isSelf = $viewer && (int) $viewer->id === (int) $user->id;
$isAdmin = $viewer ? $this->moduleUtil->is_admin($viewer, $businessId) : false;
$essentialsEnabled = $this->essentialsEnabled($businessId);
$user->loadMissing(['media', 'contactAccess', 'assignments.entity', 'assignments.orgUnit', 'roles']);
$department = $user->essentials_department_id
? Category::find($user->essentials_department_id)
: null;
$designation = $user->essentials_designation_id
? Category::find($user->essentials_designation_id)
: null;
$workLocation = $user->location_id
? BusinessLocation::find($user->location_id)
: null;
$primaryAssignment = EmploymentAssignment::where('business_id', $businessId)
->where('user_id', $user->id)
->where('is_primary', true)
->with(['entity', 'orgUnit'])
->first();
$holdingContext = $this->holdingHrmSyncService->getUserFormContext($businessId, $user);
$monthStart = Carbon::now()->startOfMonth()->format('Y-m-d');
$monthEnd = Carbon::now()->endOfMonth()->format('Y-m-d');
$yearStart = Carbon::now()->startOfYear()->format('Y-m-d');
$yearEnd = Carbon::now()->endOfYear()->format('Y-m-d');
$stats = [
'logged_hours' => 0,
'open_tasks' => 0,
'approved_leaves' => 0,
'payroll_count' => 0,
'support_tickets' => 0,
'open_support_tickets' => 0,
];
$sidebar = [
'leaves_taken' => 0,
'late_attendance' => 0,
'open_tasks' => [],
'recent_tickets' => [],
];
$attendanceRows = collect();
$leaveRows = collect();
$taskRows = collect();
$payrollRows = collect();
$ticketRows = collect();
if ($essentialsEnabled && $this->canViewEssentialsData($viewer, $user, $isSelf, $isAdmin)) {
$essentialsUtil = app(\Modules\Essentials\Utils\EssentialsUtil::class);
$stats['logged_hours'] = (float) str_replace(',', '', $essentialsUtil->getTotalWorkDuration(
'hour',
$user->id,
$businessId,
$monthStart,
$monthEnd
));
$stats['open_tasks'] = $this->countOpenTasks($user->id);
$stats['approved_leaves'] = $this->countApprovedLeaves($businessId, $user->id, $yearStart, $yearEnd);
$stats['payroll_count'] = $essentialsUtil->getPayrollQuery($businessId)
->where('transactions.expense_for', $user->id)
->count();
$sidebar['leaves_taken'] = $stats['approved_leaves'];
$sidebar['late_attendance'] = $this->countLateAttendance($businessId, $user->id, $yearStart, $yearEnd);
$sidebar['open_tasks'] = $this->getOpenTasks($user->id, 5);
$attendanceRows = $this->getRecentAttendance($businessId, $user->id, 10);
$leaveRows = $this->getRecentLeaves($businessId, $user->id, 10);
$taskRows = $this->getOpenTasks($user->id, 20);
$payrollRows = $essentialsUtil->getPayrollQuery($businessId)
->where('transactions.expense_for', $user->id)
->orderBy('transaction_date', 'desc')
->limit(10)
->get();
}
$ticketsEnabled = $this->ticketsEnabled($businessId);
if ($ticketsEnabled && $this->canViewTickets($viewer, $user, $isSelf, $isAdmin)) {
$ticketStats = $this->getTicketStats($businessId, $user->id);
$stats['support_tickets'] = $ticketStats['total'];
$stats['open_support_tickets'] = $ticketStats['open'];
$sidebar['recent_tickets'] = $this->getRecentTickets($businessId, $user->id, 5);
$ticketRows = $this->getRecentTickets($businessId, $user->id, 20);
}
$lastActivity = Activity::forSubject($user)->latest()->first();
$lastActivityAt = $lastActivity?->created_at ?? $user->updated_at;
$tabs = $this->resolveTabs($essentialsEnabled, $viewer, $user, $isSelf, $isAdmin, $options);
$mt_skills = collect();
$mt_scores = collect();
$mt_total_score_points = 0;
if ($this->managementToolsEnabled($businessId)) {
$mtUtil = app(\Modules\ManagementTools\Utils\ManagementToolsUtil::class);
$mt_skills = $mtUtil->getUserSkillsList($businessId, $user->id);
$mt_scores = $mtUtil->getUserRecentScores($businessId, $user->id, 6);
$mt_total_score_points = $mtUtil->getUserTotalScorePoints($businessId, $user->id);
}
return [
'user' => $user,
'employee_id' => 'EMP-'.str_pad((string) $user->id, 3, '0', STR_PAD_LEFT),
'avatar_url' => $this->avatarUrl($user),
'department' => $department,
'designation' => $designation,
'work_location' => $workLocation,
'primary_assignment' => $primaryAssignment ?: $holdingContext['primaryAssignment'],
'holding_url' => $holdingContext['holdingUrl'],
'last_activity_at' => $lastActivityAt,
'stats' => $stats,
'sidebar' => $sidebar,
'attendance_rows' => $attendanceRows,
'leave_rows' => $leaveRows,
'task_rows' => $taskRows,
'payroll_rows' => $payrollRows,
'ticket_rows' => $ticketRows,
'tickets_enabled' => $ticketsEnabled ?? false,
'tickets_url' => ($ticketsEnabled ?? false)
? action([\Modules\ManagementTools\Http\Controllers\TicketController::class, 'index'])
: null,
'tabs' => $tabs,
'is_self' => $isSelf,
'can_edit' => $viewer && ($isSelf || $viewer->can('user.update')),
'essentials_enabled' => $essentialsEnabled,
'attendance_url' => $essentialsEnabled
? action([\Modules\Essentials\Http\Controllers\AttendanceController::class, 'index']).'?employee_id='.$user->id
: null,
'leaves_url' => $essentialsEnabled
? action([\Modules\Essentials\Http\Controllers\EssentialsLeaveController::class, 'index']).'?user_id='.$user->id
: null,
'mt_skills' => $mt_skills,
'mt_scores' => $mt_scores,
'mt_total_score_points' => $mt_total_score_points,
'mt_my_scores_url' => $this->managementToolsEnabled($businessId)
? action([\Modules\ManagementTools\Http\Controllers\ScoreController::class, 'myScores'])
: null,
'management_tools_enabled' => $this->managementToolsEnabled($businessId),
];
}
private function essentialsEnabled(int $businessId): bool
{
return $this->moduleUtil->isModuleInstalled('Essentials')
&& $this->moduleUtil->hasThePermissionInSubscription($businessId, 'essentials_module');
}
private function canViewEssentialsData($viewer, User $user, bool $isSelf, bool $isAdmin): bool
{
if (! $viewer) {
return false;
}
if ($isSelf) {
return true;
}
if ($isAdmin || $viewer->can('essentials.crud_all_attendance')) {
return true;
}
return $viewer->can('user.view');
}
private function resolveTabs(
bool $essentialsEnabled,
$viewer,
User $user,
bool $isSelf,
bool $isAdmin,
array $options
): array {
$tabs = [
'profile' => [
'id' => 'profile_tab',
'label' => __('lang_v1.profile_tab_profile'),
'icon' => 'fa-user',
'enabled' => true,
],
];
if ($essentialsEnabled && $this->canViewEssentialsData($viewer, $user, $isSelf, $isAdmin)) {
$tabs['attendance'] = [
'id' => 'attendance_tab',
'label' => __('lang_v1.profile_tab_attendance'),
'icon' => 'fa-clock',
'enabled' => true,
];
$tabs['leaves'] = [
'id' => 'leaves_tab',
'label' => __('lang_v1.profile_tab_leaves'),
'icon' => 'fa-plane',
'enabled' => true,
];
$tabs['tasks'] = [
'id' => 'tasks_tab',
'label' => __('lang_v1.profile_tab_tasks'),
'icon' => 'fa-tasks',
'enabled' => true,
];
if ($isSelf || $isAdmin || ($viewer && $viewer->can('essentials.view_all_payroll'))) {
$tabs['payroll'] = [
'id' => 'payroll_tab',
'label' => __('lang_v1.profile_tab_payroll'),
'icon' => 'fa-money-bill',
'enabled' => true,
];
}
}
$tabs['documents'] = [
'id' => 'documents_tab',
'label' => __('lang_v1.profile_tab_documents'),
'icon' => 'fa-paperclip',
'enabled' => true,
];
if ($this->ticketsEnabled((int) $user->business_id) && $this->canViewTickets($viewer, $user, $isSelf, $isAdmin)) {
$tabs['tickets'] = [
'id' => 'tickets_tab',
'label' => __('lang_v1.profile_tab_tickets'),
'icon' => 'fa-life-ring',
'enabled' => true,
];
}
if (empty($options['hide_activities'])) {
$tabs['activities'] = [
'id' => 'activities_tab',
'label' => __('lang_v1.profile_tab_activities'),
'icon' => 'fa-history',
'enabled' => true,
];
}
if (! empty($options['show_edit_tab'])) {
$tabs['edit'] = [
'id' => 'edit_tab',
'label' => __('lang_v1.profile_tab_edit'),
'icon' => 'fa-edit',
'enabled' => true,
];
$tabs['password'] = [
'id' => 'password_tab',
'label' => __('user.change_password'),
'icon' => 'fa-lock',
'enabled' => true,
];
}
return $tabs;
}
private function avatarUrl(User $user): string
{
if (! empty($user->media->display_url)) {
return $user->media->display_url;
}
$palette = ['0074ba', '16cdc7', '36c76c', '7c3aed', 'f97316', 'ff6692', '46caeb', 'e6a800'];
$background = $palette[$user->id % count($palette)];
return 'https://ui-avatars.com/api/?name='.urlencode($user->user_full_name).'&background='.$background.'&color=fff&size=200&bold=true';
}
private function countOpenTasks(int $userId): int
{
if (! class_exists(\Modules\Essentials\Entities\ToDo::class)) {
return 0;
}
return \Modules\Essentials\Entities\ToDo::whereHas('users', function ($q) use ($userId) {
$q->where('user_id', $userId);
})->where('status', '!=', 'completed')->count();
}
private function getOpenTasks(int $userId, int $limit)
{
if (! class_exists(\Modules\Essentials\Entities\ToDo::class)) {
return collect();
}
return \Modules\Essentials\Entities\ToDo::whereHas('users', function ($q) use ($userId) {
$q->where('user_id', $userId);
})
->where('status', '!=', 'completed')
->orderBy('created_at', 'desc')
->limit($limit)
->get();
}
private function countApprovedLeaves(int $businessId, int $userId, string $start, string $end): int
{
if (! class_exists(\Modules\Essentials\Entities\EssentialsLeave::class)) {
return 0;
}
return \Modules\Essentials\Entities\EssentialsLeave::where('business_id', $businessId)
->where('user_id', $userId)
->where('status', 'approved')
->whereDate('start_date', '>=', $start)
->whereDate('start_date', '<=', $end)
->count();
}
private function getRecentLeaves(int $businessId, int $userId, int $limit)
{
if (! class_exists(\Modules\Essentials\Entities\EssentialsLeave::class)) {
return collect();
}
return \Modules\Essentials\Entities\EssentialsLeave::where('business_id', $businessId)
->where('user_id', $userId)
->with('leave_type')
->orderBy('start_date', 'desc')
->limit($limit)
->get();
}
private function getRecentAttendance(int $businessId, int $userId, int $limit)
{
if (! class_exists(\Modules\Essentials\Entities\EssentialsAttendance::class)) {
return collect();
}
return \Modules\Essentials\Entities\EssentialsAttendance::where('business_id', $businessId)
->where('user_id', $userId)
->orderBy('clock_in_time', 'desc')
->limit($limit)
->get();
}
private function countLateAttendance(int $businessId, int $userId, string $start, string $end): int
{
if (! class_exists(\Modules\Essentials\Entities\EssentialsAttendance::class)) {
return 0;
}
try {
return \Modules\Essentials\Entities\EssentialsAttendance::query()
->from('essentials_attendances as ea')
->join('essentials_shifts as es', 'es.id', '=', 'ea.essentials_shift_id')
->where('ea.business_id', $businessId)
->where('ea.user_id', $userId)
->whereNotNull('ea.clock_in_time')
->whereNotNull('es.start_time')
->whereDate('ea.clock_in_time', '>=', $start)
->whereDate('ea.clock_in_time', '<=', $end)
->whereRaw('TIME(ea.clock_in_time) > ADDTIME(es.start_time, "00:15:00")')
->count();
} catch (\Throwable $e) {
return 0;
}
}
private function ticketsEnabled(int $businessId): bool
{
return $this->managementToolsEnabled($businessId)
&& class_exists(\Modules\ManagementTools\Entities\MtUserTicket::class);
}
private function managementToolsEnabled(int $businessId): bool
{
return $this->moduleUtil->isModuleInstalled('ManagementTools')
&& $this->moduleUtil->hasThePermissionInSubscription($businessId, 'managementtools_module');
}
private function canViewTickets($viewer, User $user, bool $isSelf, bool $isAdmin): bool
{
if (! $viewer) {
return false;
}
if ($isSelf) {
return $viewer->can('managementtools.ticket_own') || $viewer->can('managementtools.ticket_manage');
}
if ($isAdmin || $viewer->can('managementtools.ticket_manage')) {
return true;
}
return $viewer->can('user.view');
}
private function getTicketStats(int $businessId, int $userId): array
{
$base = \Modules\ManagementTools\Entities\MtUserTicket::forBusiness($businessId)
->where('user_id', $userId);
return [
'total' => (clone $base)->count(),
'open' => (clone $base)->open()->count(),
];
}
private function getRecentTickets(int $businessId, int $userId, int $limit)
{
return \Modules\ManagementTools\Entities\MtUserTicket::forBusiness($businessId)
->where('user_id', $userId)
->with('assignee')
->orderByDesc('updated_at')
->limit($limit)
->get();
}
}