ensureModule('Manufacturing'); $business_id = $this->contextService->getBusinessId($request); $recipes = 0; if (Schema::hasTable('mfg_recipes')) { $recipes = DB::table('mfg_recipes')->where('business_id', $business_id)->count(); } $production = Transaction::where('business_id', $business_id) ->where('sub_type', 'production_purchase') ->count(); $recent_production = Transaction::where('business_id', $business_id) ->where('sub_type', 'production_purchase') ->orderByDesc('transaction_date') ->limit(10) ->get(['id', 'ref_no', 'transaction_date', 'final_total', 'status']); return $this->success([ 'recipes_count' => $recipes, 'production_count' => $production, 'recent_production' => $recent_production, ]); } public function repairDashboard(Request $request) { $this->ensureModule('Repair'); $business_id = $this->contextService->getBusinessId($request); $by_status = $this->repairUtil->getRepairByStatus($business_id); $by_staff = $this->repairUtil->getRepairByServiceStaff($business_id); $trending_brands = []; if (Schema::hasTable('repair_job_sheets')) { $trending_brands = DB::table('repair_job_sheets') ->leftJoin('brands', 'repair_job_sheets.brand_id', '=', 'brands.id') ->where('repair_job_sheets.business_id', $business_id) ->whereNotNull('repair_job_sheets.brand_id') ->select('brands.name as brand', DB::raw('COUNT(repair_job_sheets.id) as total')) ->groupBy('brands.id', 'brands.name') ->orderByDesc('total') ->limit(5) ->get(); } $repair_jobs = Transaction::where('business_id', $business_id) ->where('sub_type', 'repair') ->orderByDesc('transaction_date') ->limit(10) ->get(['id', 'invoice_no', 'transaction_date', 'final_total', 'status']); return $this->success([ 'by_status' => $by_status, 'by_service_staff' => $by_staff, 'trending_brands' => $trending_brands, 'recent_jobs' => $repair_jobs, ]); } public function projectDashboard(Request $request) { $this->ensureModule('Project'); $business_id = $this->contextService->getBusinessId($request); $statuses = Project::statusDropdown(); $by_status = []; if (Schema::hasTable('pjt_projects')) { $counts = DB::table('pjt_projects') ->where('business_id', $business_id) ->select('status', DB::raw('COUNT(*) as total')) ->groupBy('status') ->pluck('total', 'status'); foreach ($statuses as $key => $label) { $by_status[] = [ 'status' => $key, 'label' => $label, 'count' => (int) ($counts[$key] ?? 0), ]; } } $tasks_count = Schema::hasTable('pjt_project_tasks') ? DB::table('pjt_project_tasks')->where('business_id', $business_id)->count() : 0; $recent_projects = Schema::hasTable('pjt_projects') ? DB::table('pjt_projects') ->where('business_id', $business_id) ->orderByDesc('id') ->limit(10) ->get(['id', 'name', 'status', 'start_date', 'end_date']) : collect(); return $this->success([ 'by_status' => $by_status, 'projects_count' => array_sum(array_column($by_status, 'count')), 'tasks_count' => $tasks_count, 'recent_projects' => $recent_projects, ]); } public function assetsDashboard(Request $request) { $this->ensureModule('AssetManagement'); $business_id = $this->contextService->getBusinessId($request); $user = Auth::user(); $is_admin = $this->moduleUtil->is_admin($user, $business_id); $my_allocated = 0; $my_by_category = []; if (Schema::hasTable('asset_transactions')) { $allocated = AssetTransaction::where('receiver', $user->id) ->select( DB::raw('SUM(quantity) as total_quantity_allocated'), DB::raw('(SELECT SUM(quantity) FROM asset_transactions as AT WHERE AT.parent_id = asset_transactions.id AND AT.transaction_type="revoke") as total_revoked_quantity') )->first(); $my_allocated = ($allocated->total_quantity_allocated ?? 0) - ($allocated->total_revoked_quantity ?? 0); $my_by_category = AssetTransaction::where('asset_transactions.receiver', $user->id) ->leftJoin('assets as a', 'a.id', '=', 'asset_transactions.asset_id') ->leftJoin('categories as cat', 'a.category_id', '=', 'cat.id') ->select( DB::raw("SUM(COALESCE(asset_transactions.quantity, 0) - COALESCE((SELECT SUM(quantity) FROM asset_transactions as AT WHERE AT.parent_id = asset_transactions.id AND AT.transaction_type='revoke'),0)) as total"), 'cat.name as category' ) ->groupBy('cat.id', 'cat.name') ->get(); } $data = [ 'is_admin' => $is_admin, 'my_allocated' => $my_allocated, 'my_by_category' => $my_by_category, ]; if ($is_admin && Schema::hasTable('assets')) { $data['total_assets'] = Asset::where('business_id', $business_id) ->sum('quantity'); $data['by_category'] = Asset::where('assets.business_id', $business_id) ->leftJoin('categories as cat', 'assets.category_id', '=', 'cat.id') ->select(DB::raw('SUM(quantity) as total'), 'cat.name as category') ->groupBy('cat.id', 'cat.name') ->get(); $current_date_add30 = Carbon::now()->addDays(30)->toDateString(); $data['expiring_assets'] = Asset::where('assets.business_id', $business_id) ->leftJoin('asset_warranties as aw', 'aw.asset_id', '=', 'assets.id') ->groupBy('assets.id', 'assets.name', 'assets.asset_code') ->havingRaw("MAX(aw.end_date) <= ?", [$current_date_add30]) ->select('assets.name', 'assets.asset_code', DB::raw('MAX(aw.end_date) as max_end_date'), 'assets.id') ->limit(20) ->get(); } return $this->success($data); } public function inventoryDashboard(Request $request) { $this->ensureModule('InventoryManagement'); $business_id = $this->contextService->getBusinessId($request); if (! Schema::hasTable('inventory')) { return $this->success(['counts' => [], 'by_status' => [], 'total' => 0]); } $base = DB::table('inventory') ->join('business_locations', 'inventory.branch_id', '=', 'business_locations.id') ->where('business_locations.business_id', $business_id); $counts = (clone $base) ->select('inventory.*', 'business_locations.name as branch_name') ->orderByDesc('inventory.id') ->limit(20) ->get(); $by_status = (clone $base) ->select('inventory.status', DB::raw('COUNT(*) as total')) ->groupBy('inventory.status') ->get(); return $this->success([ 'counts' => $counts, 'by_status' => $by_status, 'total' => (clone $base)->count(), ]); } public function accountingDashboard(Request $request) { $this->ensureModule('Accounting'); $business_id = $this->contextService->getBusinessId($request); $start_date = $request->input('start_date', session()->get('financial_year.start', Carbon::now()->startOfYear()->format('Y-m-d'))); $end_date = $request->input('end_date', session()->get('financial_year.end', Carbon::now()->endOfYear()->format('Y-m-d'))); $balance_formula = $this->accountingUtil->balanceFormula(); $account_types = AccountingAccountType::accounting_primary_type(); $overview = []; if (Schema::hasTable('accounting_accounts')) { $coa_overview = AccountingAccount::leftjoin('accounting_accounts_transactions as AAT', 'AAT.accounting_account_id', '=', 'accounting_accounts.id') ->where('business_id', $business_id) ->whereDate('AAT.operation_date', '>=', $start_date) ->whereDate('AAT.operation_date', '<=', $end_date) ->select( DB::raw($balance_formula), 'accounting_accounts.account_primary_type' ) ->groupBy('accounting_accounts.account_primary_type') ->get(); foreach ($account_types as $k => $v) { $value = 0; foreach ($coa_overview as $row) { if ($row->account_primary_type == $k && ! empty($row->balance)) { $value = (float) $row->balance; } } $overview[] = [ 'type' => $k, 'label' => $v['label'], 'balance' => $value, ]; } } $accounts_count = Schema::hasTable('accounting_accounts') ? DB::table('accounting_accounts')->where('business_id', $business_id)->count() : 0; return $this->success([ 'start_date' => $start_date, 'end_date' => $end_date, 'overview' => $overview, 'accounts_count' => $accounts_count, ]); } public function superadminDashboard(Request $request) { if (! Auth::user()->can('superadmin')) { abort(403, 'Unauthorized action.'); } $not_subscribed = Business::leftjoin('subscriptions AS s', 'business.id', '=', 's.business_id') ->whereNull('s.id') ->count(); $start = Carbon::today()->subYear(); $end = Carbon::today(); $subscriptions = Subscription::whereRaw('DATE(created_at) BETWEEN ? AND ?', [$start, $end]) ->select('package_price', 'created_at') ->orderBy('created_at') ->get(); $monthly = []; foreach ($subscriptions as $value) { $month_year = format_shamsi_chart_label($value->created_at, 'M-Y', 'jMMMM-jYYYY'); if (! isset($monthly[$month_year])) { $monthly[$month_year] = 0; } $monthly[$month_year] += (float) $value->package_price; } return $this->success([ 'not_subscribed' => $not_subscribed, 'businesses_count' => Business::count(), 'packages_count' => Schema::hasTable('packages') ? DB::table('packages')->count() : 0, 'subscriptions_count' => Subscription::count(), 'monthly_subscriptions' => $monthly, ]); } public function superadminStats(Request $request) { if (! Auth::user()->can('superadmin')) { abort(403, 'Unauthorized action.'); } $start_date = $request->input('start_date', Carbon::today()->startOfMonth()->format('Y-m-d')); $end_date = $request->input('end_date', Carbon::today()->format('Y-m-d')); $subscription = Subscription::whereRaw('DATE(created_at) BETWEEN ? AND ?', [$start_date, $end_date]) ->where('status', 'approved') ->select(DB::raw('SUM(package_price) as total')) ->first() ->total ?? 0; $registrations = Business::whereRaw('DATE(created_at) BETWEEN ? AND ?', [$start_date, $end_date]) ->count(); return $this->success([ 'new_subscriptions' => (float) $subscription, 'new_registrations' => $registrations, ]); } public function spreadsheetDashboard(Request $request) { $this->ensureModule('Spreadsheet'); $business_id = $this->contextService->getBusinessId($request); if (! Schema::hasTable('sheet_spreadsheets')) { return $this->success(['folders' => [], 'spreadsheets' => []]); } $folders = Schema::hasTable('categories') ? DB::table('categories') ->where('business_id', $business_id) ->where('category_type', 'spreadsheet') ->get(['id', 'name']) : collect(); $spreadsheets = DB::table('sheet_spreadsheets') ->where('business_id', $business_id) ->orderByDesc('id') ->get(['id', 'name', 'folder_id', 'created_at', 'updated_at']); return $this->success([ 'folders' => $folders, 'spreadsheets' => $spreadsheets, ]); } protected function ensureModule(string $module): void { if (! $this->moduleUtil->isModuleInstalled($module)) { abort(403, 'Unauthorized action.'); } } }