import json import os from decimal import Decimal from shared.db import current_week, get_orders, get_roster, get_summary from shared.slack import post_channel_message, send_dm class DecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, Decimal): return float(o) return super().default(o) def lambda_handler(event, context): event_type = event.get("event", "") if event_type == "menu_published": return handle_menu_published(event) elif event_type == "orders_aggregated": return handle_orders_aggregated(event) elif event_type == "reminder": return handle_reminder(event) elif event_type == "order_confirmed": return handle_order_confirmed(event) return {"error": f"Unknown event type: {event_type}"} def handle_menu_published(event): form_url = os.environ.get("FORM_URL", "") week = event.get("week", current_week()) meal_count = event.get("meal_count", "") text = "This week's meal order is open! Deadline: Thursday at 11:59pm." blocks = [ { "type": "header", "text": {"type": "plain_text", "text": "Meal Order Open"}, }, { "type": "section", "text": { "type": "mrkdwn", "text": ( f"*<{form_url}|Place your order>*\n\n" f"*Deadline:* Thursday at 11:59pm\n" f"*Menu:* {meal_count} meals available" ), }, }, ] post_channel_message(text, blocks) return {"status": "notified", "event": "menu_published", "week": week} def handle_orders_aggregated(event): week = event.get("week", current_week()) summary = get_summary(week) if not summary: return {"status": "no_summary", "week": week} total_employees = int(summary.get("total_employees", 0)) total_meals = int(summary.get("total_meals", 0)) grand_total = float(summary.get("grand_total", 0)) employee_total = float(summary.get("employee_total", grand_total)) meal_lines = [] for m in summary.get("meals", []): meal_lines.append(f"{m['meal']}: *{int(m['quantity'])}*") meal_list = "\n".join(meal_lines) has_subsidy = employee_total < grand_total totals_text = ( f"*${grand_total:.2f}* order total (bulk rate)\n" f"*${employee_total:.2f}* payroll deductions" + (f"\n*${grand_total - employee_total:.2f}* company subsidy" if has_subsidy else "") ) text = f"Meal orders closed for {week}. {total_employees} employees, {total_meals} meals, ${grand_total:.2f} total." blocks = [ { "type": "header", "text": {"type": "plain_text", "text": f"Meal Orders Closed — {week}"}, }, { "type": "section", "text": { "type": "mrkdwn", "text": ( f"*{total_employees}* employees ordered\n" f"*{total_meals}* total meals\n" f"{totals_text}" ), }, }, {"type": "divider"}, { "type": "section", "text": { "type": "mrkdwn", "text": f"*Order for Redefine Meals:*\n{meal_list}", }, }, ] post_channel_message(text, blocks) return {"status": "notified", "event": "orders_aggregated", "week": week} def handle_order_confirmed(event): email = event.get("employee_email", "").lower() name = event.get("employee_name", "") items = event.get("items", []) total = float(event.get("total", 0)) week = event.get("week", current_week()) roster = get_roster() employee = next((e for e in roster if e["email"].lower() == email), None) if not employee or not employee.get("slack_user_id"): return {"status": "no_slack_id", "email": email} item_lines = [] for item in items: qty = int(item.get("quantity", 0)) price = float(item.get("price", 0)) item_lines.append(f"{item['name']} x{qty} — ${price * qty:.2f}") item_list = "\n".join(item_lines) send_dm( employee["slack_user_id"], f"Order confirmed for {week}: ${total:.2f} total.", blocks=[ { "type": "header", "text": {"type": "plain_text", "text": f"Order Confirmed — {week}"}, }, { "type": "section", "text": { "type": "mrkdwn", "text": ( f"Hey {name.split()[0]}! Your meal order has been submitted.\n\n" f"{item_list}\n\n" f"*Total: ${total:.2f}*" ), }, }, ], ) return {"status": "confirmed", "week": week, "employee": name} def handle_reminder(event): week = event.get("week", current_week()) form_url = os.environ.get("FORM_URL", "") roster = get_roster() if not roster: return {"status": "no_roster"} orders = get_orders(week) ordered_emails = {o["employee_email"].lower() for o in orders} missing = [emp for emp in roster if emp["email"].lower() not in ordered_emails] dm_count = 0 for emp in missing: slack_id = emp.get("slack_user_id") if not slack_id: continue send_dm( slack_id, f"Meal orders close today at 11:59pm. Place your order: {form_url}", blocks=[ { "type": "section", "text": { "type": "mrkdwn", "text": ( f"Hey {emp['name'].split()[0]}! Meal orders close today at *11:59pm*.\n\n" f"*<{form_url}|Place your order>*" ), }, } ], ) dm_count += 1 return { "status": "reminders_sent", "week": week, "dm_count": dm_count, "missing_count": len(missing), }