| | 142 | |
| | 143 | |
| | 144 | === Stored procedure |
| | 145 | |
| | 146 | {{{ |
| | 147 | CREATE OR REPLACE FUNCTION get_analytics_stats( |
| | 148 | p_start_date DATE, |
| | 149 | p_end_date DATE |
| | 150 | ) |
| | 151 | RETURNS JSON AS $$ |
| | 152 | DECLARE |
| | 153 | result JSON; |
| | 154 | BEGIN |
| | 155 | SELECT json_build_object( |
| | 156 | 'dailyRevenue', ( |
| | 157 | SELECT json_agg(r) |
| | 158 | FROM ( |
| | 159 | SELECT DATE(timestamp) AS payment_date, SUM(amount) AS total_revenue |
| | 160 | FROM payments |
| | 161 | WHERE DATE(timestamp) BETWEEN p_start_date AND p_end_date |
| | 162 | GROUP BY DATE(timestamp) |
| | 163 | ORDER BY payment_date |
| | 164 | ) r |
| | 165 | ), |
| | 166 | 'dailyOrders', ( |
| | 167 | SELECT json_agg(r) |
| | 168 | FROM ( |
| | 169 | SELECT DATE(o.datetime) AS order_date, COUNT(o.id) AS total_orders, AVG(p.amount) AS avg_order_value |
| | 170 | FROM orders o |
| | 171 | JOIN payments p ON o.id = p.order_id |
| | 172 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 173 | GROUP BY DATE(o.datetime) |
| | 174 | ORDER BY order_date |
| | 175 | ) r |
| | 176 | ), |
| | 177 | 'orderDistribution', ( |
| | 178 | SELECT json_agg(r) |
| | 179 | FROM ( |
| | 180 | SELECT c.name AS category, COUNT(oi.id) AS total_items_ordered |
| | 181 | FROM order_items oi |
| | 182 | JOIN products p ON oi.product_id = p.id |
| | 183 | JOIN categories c ON p.category_id = c.id |
| | 184 | JOIN orders o ON o.id = oi.order_id -- filter by order date |
| | 185 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 186 | GROUP BY c.name |
| | 187 | ORDER BY total_items_ordered DESC |
| | 188 | ) r |
| | 189 | ), |
| | 190 | 'topProducts', ( |
| | 191 | SELECT json_agg(r) |
| | 192 | FROM ( |
| | 193 | SELECT p.name AS product, SUM(oi.quantity) AS total_quantity_sold |
| | 194 | FROM order_items oi |
| | 195 | JOIN products p ON oi.product_id = p.id |
| | 196 | JOIN orders o ON o.id = oi.order_id -- apply date filter |
| | 197 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 198 | GROUP BY p.name |
| | 199 | ORDER BY total_quantity_sold DESC |
| | 200 | LIMIT 10 |
| | 201 | ) r |
| | 202 | ), |
| | 203 | 'onlineVsTabOrders', ( |
| | 204 | SELECT json_agg(r) |
| | 205 | FROM ( |
| | 206 | SELECT 'Online Orders' AS order_type, COUNT(*) AS total_orders |
| | 207 | FROM online_orders oo |
| | 208 | JOIN orders o ON oo.order_id = o.id |
| | 209 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 210 | UNION ALL |
| | 211 | SELECT 'Tab Orders' AS order_type, COUNT(*) AS total_orders |
| | 212 | FROM tab_orders tord |
| | 213 | JOIN orders o ON tord.order_id = o.id |
| | 214 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 215 | ) r |
| | 216 | ), |
| | 217 | 'revenueSplit', ( |
| | 218 | SELECT json_agg(r) |
| | 219 | FROM ( |
| | 220 | SELECT 'Online Orders' AS order_type, SUM(p.amount) AS total_revenue |
| | 221 | FROM orders o |
| | 222 | JOIN payments p ON o.id = p.order_id |
| | 223 | JOIN online_orders oo ON o.id = oo.order_id |
| | 224 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 225 | UNION ALL |
| | 226 | SELECT 'Tab Orders' AS order_type, SUM(p.amount) AS total_revenue |
| | 227 | FROM orders o |
| | 228 | JOIN payments p ON o.id = p.order_id |
| | 229 | JOIN tab_orders tord ON o.id = tord.order_id |
| | 230 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 231 | ) r |
| | 232 | ), |
| | 233 | 'avgShiftDuration', ( |
| | 234 | SELECT json_agg(r) |
| | 235 | FROM ( |
| | 236 | SELECT manager_id, |
| | 237 | AVG(EXTRACT(EPOCH FROM (end_time - start_time)) / 3600) AS avg_shift_duration_hours |
| | 238 | FROM shifts |
| | 239 | WHERE DATE(start_time) BETWEEN p_start_date AND p_end_date |
| | 240 | GROUP BY manager_id |
| | 241 | ) r |
| | 242 | ), |
| | 243 | 'ordersByFrontStaff', ( |
| | 244 | SELECT json_agg(r) |
| | 245 | FROM ( |
| | 246 | SELECT fs.employee_id, COUNT(tord.order_id) AS total_orders_managed |
| | 247 | FROM tab_orders tord |
| | 248 | JOIN front_staff fs ON tord.front_staff_id = fs.employee_id |
| | 249 | JOIN orders o ON tord.order_id = o.id |
| | 250 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 251 | GROUP BY fs.employee_id |
| | 252 | ORDER BY total_orders_managed DESC |
| | 253 | ) r |
| | 254 | ), |
| | 255 | 'avgTipPercentage', ( |
| | 256 | SELECT json_agg(r) |
| | 257 | FROM ( |
| | 258 | SELECT sr.name AS staff_role, AVG(fs.tip_percent) AS avg_tip_percent |
| | 259 | FROM front_staff fs |
| | 260 | JOIN staff_roles sr ON fs.staff_role_id = sr.id |
| | 261 | GROUP BY sr.name |
| | 262 | ) r |
| | 263 | ), |
| | 264 | 'inventoryTurnover', ( |
| | 265 | SELECT json_agg(r) |
| | 266 | FROM ( |
| | 267 | SELECT p.name AS product, inv.quantity AS current_inventory, |
| | 268 | SUM(oi.quantity) AS total_quantity_ordered, |
| | 269 | CASE WHEN inv.quantity = 0 THEN NULL ELSE SUM(oi.quantity)::decimal / inv.quantity END AS turnover_ratio |
| | 270 | FROM products p |
| | 271 | JOIN inventories inv ON p.id = inv.product_id |
| | 272 | JOIN order_items oi ON p.id = oi.product_id |
| | 273 | JOIN orders o ON o.id = oi.order_id |
| | 274 | WHERE DATE(o.datetime) BETWEEN p_start_date AND p_end_date |
| | 275 | GROUP BY p.name, inv.quantity |
| | 276 | ORDER BY turnover_ratio DESC |
| | 277 | ) r |
| | 278 | ), |
| | 279 | 'mostPopularTable', ( |
| | 280 | SELECT json_build_object( |
| | 281 | 'table_number', table_number, |
| | 282 | 'reservation_count', reservation_count |
| | 283 | ) |
| | 284 | FROM ( |
| | 285 | SELECT table_number, COUNT(*) AS reservation_count |
| | 286 | FROM frontstaff_managed_reservations |
| | 287 | WHERE DATE(datetime) BETWEEN p_start_date AND p_end_date |
| | 288 | GROUP BY table_number |
| | 289 | ORDER BY reservation_count DESC |
| | 290 | LIMIT 1 |
| | 291 | ) sub |
| | 292 | ), |
| | 293 | 'reservationsByDayOfWeek', ( |
| | 294 | SELECT json_agg(r) |
| | 295 | FROM ( |
| | 296 | SELECT EXTRACT(DOW FROM datetime) AS day_of_week, COUNT(*) AS total_reservations |
| | 297 | FROM reservations |
| | 298 | WHERE DATE(datetime) BETWEEN p_start_date AND p_end_date |
| | 299 | GROUP BY day_of_week |
| | 300 | ORDER BY total_reservations DESC |
| | 301 | ) r |
| | 302 | ) |
| | 303 | ) INTO result; |
| | 304 | |
| | 305 | RETURN result; |
| | 306 | END; |
| | 307 | $$ LANGUAGE plpgsql; |
| | 308 | }}} |
| | 309 | {{{ |
| | 310 | SELECT get_analytics_stats('2025-01-01', '2025-01-31'); |
| | 311 | }}} |