commonUtil = $commonUtil; } /** * Convert user-entered date (Jalali or Gregorian) to MySQL Y-m-d. */ public function parseInputDate(?string $date): ?string { if (empty($date)) { return null; } if (function_exists('jalali_to_gregorian')) { $converted = jalali_to_gregorian($date, session('business.date_format', 'Y/m/d'), false); if (! empty($converted) && preg_match('/^\d{4}-\d{2}-\d{2}/', $converted)) { return $converted; } } return $this->commonUtil->uf_date($date); } /** * Convert user-entered datetime to MySQL Y-m-d H:i:s. */ public function parseInputDateTime(?string $date): ?string { if (empty($date)) { return null; } if (function_exists('jalali_to_gregorian')) { $converted = jalali_to_gregorian($date, session('business.date_format', 'Y/m/d'), true); if (! empty($converted) && preg_match('/^\d{4}-\d{2}-\d{2}/', $converted)) { return $converted; } } return $this->commonUtil->uf_date($date, true); } /** * Format a stored date for display/inputs — always Jalali in this module. */ public function formatForInput($date): ?string { if (empty($date)) { return null; } try { $carbon = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date); $format = session('business.date_format', 'Y/m/d'); $formatted = format_jalali_date($carbon, $format); return function_exists('persian_number') ? persian_number($formatted) : $formatted; } catch (\Exception $e) { return format_date($date); } } /** * Format a stored datetime for display/inputs — always Jalali in this module. */ public function formatDateTimeForInput($date): ?string { if (empty($date)) { return null; } try { $carbon = $date instanceof \Carbon\Carbon ? $date->copy() : \Carbon\Carbon::parse($date); $format = session('business.date_format', 'Y/m/d'); $time_format = session('business.time_format', 24) == 12 ? ' h:i A' : ' H:i'; $formatted = format_jalali_date($carbon, $format.$time_format); return function_exists('persian_number') ? persian_number($formatted) : $formatted; } catch (\Exception $e) { return format_datetime($date); } } /** * Shorthand for views — format date in Jalali. */ public function formatDate($date): ?string { return $this->formatForInput($date); } /** * Shorthand for views — format datetime in Jalali. */ public function formatDateTime($date): ?string { return $this->formatDateTimeForInput($date); } /** * Build hierarchical tree from flat collection. */ public function buildTree(Collection $items, $parent_id = null, $depth = 0): array { $branch = []; foreach ($items->where('parent_id', $parent_id) as $item) { $node = $item->toArray(); $node['depth'] = $depth; $node['children'] = $this->buildTree($items, $item->id, $depth + 1); $branch[] = $node; } return $branch; } /** * Flatten tree for table display with indentation. */ public function flattenTree(array $tree, &$flat = []): array { foreach ($tree as $node) { $children = $node['children'] ?? []; unset($node['children']); $flat[] = $node; if (! empty($children)) { $this->flattenTree($children, $flat); } } return $flat; } public function getSkillsDropdown($business_id, $exclude_id = null): array { $query = MtSkill::forBusiness($business_id)->orderBy('title'); if ($exclude_id) { $query->where('id', '!=', $exclude_id); } return $query->pluck('title', 'id')->toArray(); } public function getToolsDropdown($business_id, $exclude_id = null): array { $query = MtTool::forBusiness($business_id)->orderBy('title'); if ($exclude_id) { $query->where('id', '!=', $exclude_id); } return $query->pluck('title', 'id')->toArray(); } public function parseToolAttributes(array $names, array $values): array { $attributes = []; foreach ($names as $key => $name) { $name = trim((string) $name); if ($name === '') { continue; } $attributes[$name] = $values[$key] ?? ''; } return $attributes; } /** * Assigned skills for a user: [skill_id => skill_level]. */ public function getUserAssignedSkills(int $business_id, int $user_id): array { return DB::table('mt_user_skill') ->where('business_id', $business_id) ->where('user_id', $user_id) ->pluck('skill_level', 'skill_id') ->toArray(); } /** * Skills with title and level for profile display. */ public function getUserSkillsList(int $business_id, int $user_id): Collection { return DB::table('mt_user_skill as us') ->join('mt_skills as s', 's.id', '=', 'us.skill_id') ->where('us.business_id', $business_id) ->where('us.user_id', $user_id) ->select('s.id', 's.title', 'us.skill_level') ->orderBy('s.title') ->get(); } /** * Sync employee skills (replace all). */ public function syncUserSkills(int $business_id, int $user_id, ?array $skill_ids, ?array $skill_levels = []): void { DB::table('mt_user_skill') ->where('business_id', $business_id) ->where('user_id', $user_id) ->delete(); foreach ($skill_ids ?? [] as $skill_id) { DB::table('mt_user_skill')->insert([ 'business_id' => $business_id, 'skill_id' => (int) $skill_id, 'user_id' => $user_id, 'skill_level' => (int) ($skill_levels[$skill_id] ?? 1), 'created_at' => now(), 'updated_at' => now(), ]); } } /** * Sync employee tools (replace all). */ public function syncUserTools(int $business_id, int $user_id, ?array $tool_ids): void { DB::table('mt_user_tool') ->where('business_id', $business_id) ->where('user_id', $user_id) ->delete(); foreach ($tool_ids ?? [] as $tool_id) { DB::table('mt_user_tool')->insert([ 'business_id' => $business_id, 'tool_id' => (int) $tool_id, 'user_id' => $user_id, 'created_at' => now(), 'updated_at' => now(), ]); } } /** * Report: employee count per skill. */ public function getSkillsEmployeeReport(int $business_id): array { $skills = MtSkill::forBusiness($business_id)->orderBy('title')->get(); $total_employees = DB::table('users') ->where('business_id', $business_id) ->where('user_type', 'user') ->where('allow_login', 1) ->whereNull('deleted_at') ->count(); $assigned_user_ids = DB::table('mt_user_skill') ->where('business_id', $business_id) ->distinct() ->pluck('user_id'); $rows = $skills->map(function ($skill) use ($business_id) { $employees = DB::table('mt_user_skill as us') ->join('users as u', 'u.id', '=', 'us.user_id') ->where('us.business_id', $business_id) ->where('us.skill_id', $skill->id) ->where('u.user_type', 'user') ->where('u.allow_login', 1) ->whereNull('u.deleted_at') ->select('u.id', 'u.first_name', 'u.last_name', 'u.surname', 'us.skill_level') ->orderBy('u.first_name') ->get(); return (object) [ 'skill' => $skill, 'employee_count' => $employees->count(), 'employees' => $employees, ]; }); return [ 'rows' => $rows, 'total_employees' => $total_employees, 'employees_with_skills' => $assigned_user_ids->count(), 'employees_without_skills' => max(0, $total_employees - $assigned_user_ids->count()), 'unassigned_skills' => $rows->where('employee_count', 0)->count(), ]; } public function getUserRecentScores(int $business_id, int $user_id, int $limit = 6) { return \Modules\ManagementTools\Entities\MtUserScore::forBusiness($business_id) ->where('user_id', $user_id) ->with('scoreCard') ->orderByDesc('created_at') ->limit($limit) ->get(); } public function getUserTotalScorePoints(int $business_id, int $user_id): float { return \Modules\ManagementTools\Entities\MtUserScore::forBusiness($business_id) ->where('user_id', $user_id) ->with('scoreCard') ->get() ->sum(function ($s) { return $s->scoreCard ? (float) $s->scoreCard->points : (float) $s->score; }); } }