| 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 | | }}} |