MAX_FILE_SIZE) { throw new Exception('File too large. Maximum size: ' . (MAX_FILE_SIZE / 1024 / 1024) . 'MB'); } // Create upload directory if it doesn't exist if (!is_dir($uploadDir)) { mkdir($uploadDir, 0755, true); } // Generate unique filename $filename = generateUniqueFilename($file['name']); $filepath = $uploadDir . $filename; // Move uploaded file if (!move_uploaded_file($file['tmp_name'], $filepath)) { throw new Exception('Failed to save uploaded file'); } return $filepath; } /** * Get dashboard statistics */ function getDashboardStats($userId = null) { // Total Revenue $revenueQuery = "SELECT COALESCE(SUM(amount), 0) as total_revenue FROM sales WHERE 1=1"; $params = []; if ($userId) { $revenueQuery .= " AND created_by = ?"; $params[] = $userId; } $totalRevenue = fetchRow($revenueQuery, $params)['total_revenue']; // Total Expenses $expenseQuery = "SELECT COALESCE(SUM(amount), 0) as total_expenses FROM expenses WHERE 1=1"; $expenseParams = []; if ($userId) { $expenseQuery .= " AND created_by = ?"; $expenseParams[] = $userId; } $totalExpenses = fetchRow($expenseQuery, $expenseParams)['total_expenses']; // Monthly Revenue (current month) $monthlyRevenueQuery = "SELECT COALESCE(SUM(amount), 0) as monthly_revenue FROM sales WHERE MONTH(sale_date) = MONTH(CURRENT_DATE()) AND YEAR(sale_date) = YEAR(CURRENT_DATE())"; $monthlyParams = []; if ($userId) { $monthlyRevenueQuery .= " AND created_by = ?"; $monthlyParams[] = $userId; } $monthlyRevenue = fetchRow($monthlyRevenueQuery, $monthlyParams)['monthly_revenue']; // Active Investments/Loans $investmentQuery = "SELECT COALESCE(SUM(amount), 0) as total_investments FROM investments WHERE status = 'active'"; $totalInvestments = fetchRow($investmentQuery)['total_investments']; // Profit Calculation $profit = $totalRevenue - $totalExpenses; $profitMargin = $totalRevenue > 0 ? ($profit / $totalRevenue) * 100 : 0; return [ 'total_revenue' => $totalRevenue, 'total_expenses' => $totalExpenses, 'monthly_revenue' => $monthlyRevenue, 'total_investments' => $totalInvestments, 'profit' => $profit, 'profit_margin' => $profitMargin ]; } /** * Get recent transactions for dashboard */ function getRecentTransactions($limit = 10, $userId = null) { $query = " (SELECT 'sale' as type, id, title, amount, sale_date as transaction_date, customer_name as party_name, created_at FROM sales WHERE 1=1" . ($userId ? " AND created_by = ?" : "") . ") UNION ALL (SELECT 'expense' as type, id, title, amount, expense_date as transaction_date, '' as party_name, created_at FROM expenses WHERE 1=1" . ($userId ? " AND created_by = ?" : "") . ") ORDER BY transaction_date DESC, created_at DESC LIMIT ?"; $params = []; if ($userId) { $params[] = $userId; $params[] = $userId; } $params[] = $limit; return fetchAll($query, $params); } /** * Get business segment performance */ function getSegmentPerformance() { $query = "SELECT bs.name as segment_name, COALESCE(SUM(s.amount), 0) as revenue, COALESCE(SUM(e.amount), 0) as expenses, (COALESCE(SUM(s.amount), 0) - COALESCE(SUM(e.amount), 0)) as profit FROM business_segments bs LEFT JOIN sales s ON bs.id = s.segment_id LEFT JOIN expenses e ON bs.id = e.segment_id WHERE bs.status = 'active' GROUP BY bs.id, bs.name ORDER BY revenue DESC"; return fetchAll($query); } /** * Get monthly trends data for charts */ function getMonthlyTrends($year = null) { if (!$year) { $year = date('Y'); } $query = "SELECT MONTH(t.transaction_date) as month, MONTHNAME(t.transaction_date) as month_name, SUM(CASE WHEN t.type = 'sale' THEN t.amount ELSE 0 END) as revenue, SUM(CASE WHEN t.type = 'expense' THEN t.amount ELSE 0 END) as expenses FROM ( SELECT 'sale' as type, amount, sale_date as transaction_date FROM sales UNION ALL SELECT 'expense' as type, amount, expense_date as transaction_date FROM expenses ) t WHERE YEAR(t.transaction_date) = ? GROUP BY MONTH(t.transaction_date), MONTHNAME(t.transaction_date) ORDER BY MONTH(t.transaction_date)"; return fetchAll($query, [$year]); } /** * Search function for sales, expenses, etc. */ function performSearch($query, $table, $searchFields, $userId = null, $limit = 50) { $searchConditions = []; $params = []; // Build search conditions foreach ($searchFields as $field) { $searchConditions[] = "$field LIKE ?"; $params[] = "%$query%"; } $sql = "SELECT * FROM $table WHERE (" . implode(' OR ', $searchConditions) . ")"; // Add user filter if provided if ($userId) { $sql .= " AND created_by = ?"; $params[] = $userId; } $sql .= " ORDER BY created_at DESC LIMIT ?"; $params[] = $limit; return fetchAll($sql, $params); } /** * Generate report data */ function generateReport($type, $startDate, $endDate, $segmentId = null) { $params = [$startDate, $endDate]; switch ($type) { case 'sales': $query = "SELECT s.*, bs.name as segment_name, c.name as category_name FROM sales s LEFT JOIN business_segments bs ON s.segment_id = bs.id LEFT JOIN categories c ON s.category_id = c.id WHERE s.sale_date BETWEEN ? AND ?"; break; case 'expenses': $query = "SELECT e.*, bs.name as segment_name, c.name as category_name FROM expenses e LEFT JOIN business_segments bs ON e.segment_id = bs.id LEFT JOIN categories c ON e.category_id = c.id WHERE e.expense_date BETWEEN ? AND ?"; break; case 'profit_loss': $query = "SELECT 'Revenue' as type, SUM(amount) as total_amount, COUNT(*) as transaction_count FROM sales WHERE sale_date BETWEEN ? AND ? UNION ALL SELECT 'Expenses' as type, SUM(amount) as total_amount, COUNT(*) as transaction_count FROM expenses WHERE expense_date BETWEEN ? AND ?"; $params = [$startDate, $endDate, $startDate, $endDate]; break; default: throw new Exception('Invalid report type'); } // Add segment filter if provided if ($segmentId && in_array($type, ['sales', 'expenses'])) { $query .= " AND segment_id = ?"; $params[] = $segmentId; } if ($type !== 'profit_loss') { $query .= " ORDER BY " . ($type === 'sales' ? 'sale_date' : 'expense_date') . " DESC"; } return fetchAll($query, $params); } /** * Export data to CSV */ function exportToCSV($data, $filename, $headers = null) { header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Cache-Control: no-cache, must-revalidate'); header('Pragma: no-cache'); $output = fopen('php://output', 'w'); // Add headers if provided or use array keys from first row if ($headers) { fputcsv($output, $headers); } elseif (!empty($data)) { fputcsv($output, array_keys($data[0])); } // Add data rows foreach ($data as $row) { fputcsv($output, $row); } fclose($output); exit; } /** * Calculate loan EMI */ function calculateEMI($principal, $rate, $tenure) { $monthlyRate = $rate / (12 * 100); $emi = ($principal * $monthlyRate * pow(1 + $monthlyRate, $tenure)) / (pow(1 + $monthlyRate, $tenure) - 1); return round($emi, 2); } /** * Send email notification (basic implementation) */ function sendEmail($to, $subject, $message, $isHTML = true) { $headers = [ 'From: ' . APP_NAME . ' ', 'Reply-To: noreply@' . parse_url(APP_URL, PHP_URL_HOST), 'X-Mailer: PHP/' . phpversion() ]; if ($isHTML) { $headers[] = 'Content-Type: text/html; charset=UTF-8'; $headers[] = 'MIME-Version: 1.0'; } return mail($to, $subject, $message, implode("\r\n", $headers)); } /** * Log system activity */ function logSystemActivity($activity, $details = '', $userId = null) { if (!$userId && isLoggedIn()) { $userId = $_SESSION['user_id']; } $data = [ 'user_id' => $userId, 'activity' => $activity, 'details' => $details, 'ip_address' => $_SERVER['REMOTE_ADDR'] ?? '', 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'created_at' => date('Y-m-d H:i:s') ]; try { insertData('activity_logs', $data); } catch (Exception $e) { error_log("Failed to log activity: " . $e->getMessage()); } } /** * Get application settings */ function getAppSettings() { static $settings = null; if ($settings === null) { $settingsData = fetchAll("SELECT setting_key, setting_value FROM settings"); $settings = []; foreach ($settingsData as $setting) { $settings[$setting['setting_key']] = $setting['setting_value']; } } return $settings; } /** * Update application setting */ function updateAppSetting($key, $value) { $existing = fetchRow("SELECT id FROM settings WHERE setting_key = ?", [$key]); if ($existing) { updateData('settings', ['setting_value' => $value], 'setting_key = ?', [$key]); } else { insertData('settings', [ 'setting_key' => $key, 'setting_value' => $value, 'description' => '' ]); } // Clear cached settings getAppSettings.clear(); } /** * Format number for Indian currency */ function formatIndianCurrency($number) { return '₹' . number_format($number, 2); } /** * Convert number to words (for invoices, etc.) */ function numberToWords($number) { $words = [ 0 => 'Zero', 1 => 'One', 2 => 'Two', 3 => 'Three', 4 => 'Four', 5 => 'Five', 6 => 'Six', 7 => 'Seven', 8 => 'Eight', 9 => 'Nine', 10 => 'Ten', 11 => 'Eleven', 12 => 'Twelve', 13 => 'Thirteen', 14 => 'Fourteen', 15 => 'Fifteen', 16 => 'Sixteen', 17 => 'Seventeen', 18 => 'Eighteen', 19 => 'Nineteen', 20 => 'Twenty', 30 => 'Thirty', 40 => 'Forty', 50 => 'Fifty', 60 => 'Sixty', 70 => 'Seventy', 80 => 'Eighty', 90 => 'Ninety' ]; if ($number < 21) { return $words[$number]; } elseif ($number < 100) { return $words[10 * floor($number / 10)] . ($number % 10 ? ' ' . $words[$number % 10] : ''); } elseif ($number < 1000) { return $words[floor($number / 100)] . ' Hundred' . ($number % 100 ? ' ' . numberToWords($number % 100) : ''); } elseif ($number < 100000) { return numberToWords(floor($number / 1000)) . ' Thousand' . ($number % 1000 ? ' ' . numberToWords($number % 1000) : ''); } elseif ($number < 10000000) { return numberToWords(floor($number / 100000)) . ' Lakh' . ($number % 100000 ? ' ' . numberToWords($number % 100000) : ''); } else { return numberToWords(floor($number / 10000000)) . ' Crore' . ($number % 10000000 ? ' ' . numberToWords($number % 10000000) : ''); } } /** * Generate invoice number */ function generateInvoiceNumber($prefix = 'KA') { $year = date('Y'); $month = date('m'); // Get last invoice number for this month $lastInvoice = fetchRow( "SELECT invoice_number FROM sales WHERE invoice_number LIKE ? ORDER BY invoice_number DESC LIMIT 1", [$prefix . $year . $month . '%'] ); $sequence = 1; if ($lastInvoice) { $lastSequence = (int) substr($lastInvoice['invoice_number'], -4); $sequence = $lastSequence + 1; } return $prefix . $year . $month . str_pad($sequence, 4, '0', STR_PAD_LEFT); } /** * Check if user has permission */ function hasPermission($permission, $userId = null) { if (!$userId && isLoggedIn()) { $userId = $_SESSION['user_id']; } if (!$userId) { return false; } $user = fetchRow("SELECT role FROM users WHERE id = ?", [$userId]); if (!$user) { return false; } // Define role permissions $permissions = [ 'admin' => ['*'], // Admin has all permissions 'manager' => ['view_dashboard', 'manage_sales', 'manage_expenses', 'view_reports', 'manage_categories'], 'staff' => ['view_dashboard', 'add_sales', 'add_expenses', 'view_own_data'] ]; $userPermissions = $permissions[$user['role']] ?? []; return in_array('*', $userPermissions) || in_array($permission, $userPermissions); } /** * Backup database */ function backupDatabase($filename = null) { if (!$filename) { $filename = 'backup_' . date('Y-m-d_H-i-s') . '.sql'; } $backupDir = __DIR__ . '/../backups/'; if (!is_dir($backupDir)) { mkdir($backupDir, 0755, true); } $backupFile = $backupDir . $filename; $command = sprintf( 'mysqldump --user=%s --password=%s --host=%s %s > %s', DB_USER, DB_PASS, DB_HOST, DB_NAME, escapeshellarg($backupFile) ); exec($command, $output, $return); return $return === 0 ? $backupFile : false; } /** * Get file size in human readable format */ function humanFileSize($size, $precision = 2) { for ($i = 0; ($size / 1024) > 0.9; $i++, $size /= 1024) {} return round($size, $precision) . ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'][$i]; } /** * Clean old log files */ function cleanOldLogs($days = 30) { $cutoffDate = date('Y-m-d H:i:s', strtotime("-$days days")); try { executeQuery("DELETE FROM activity_logs WHERE created_at < ?", [$cutoffDate]); return true; } catch (Exception $e) { error_log("Failed to clean old logs: " . $e->getMessage()); return false; } } ?>