"""Output generators for custom food orders: receipt, customer message, pickup reminder, ingredient shopping list. Pure string formatting from a normalized order dict — no model calls, no I/O. Storage keys are generic; their meaning here: services=items, next_appointment=pickup, supplies=ingredients, amount_paid=deposit/payment. """ from __future__ import annotations from datetime import datetime from config import BUSINESS_NAME, CURRENCY def money(x) -> str: try: return f"{CURRENCY}{float(x or 0):,.2f}" except (TypeError, ValueError): return f"{CURRENCY}0.00" def _balance(order: dict) -> float: return round(float(order.get("amount_charged") or 0) - float(order.get("amount_paid") or 0), 2) def _item_lines(order: dict) -> str: items = order.get("services") or [] if not items: return " • Order" return "\n".join(f" • {s}" for s in items) def invoice(order: dict) -> str: """Order receipt for the customer.""" customer = order.get("customer") or "Customer" date = (order.get("created_at") or datetime.now().isoformat())[:10] balance = _balance(order) paid = float(order.get("amount_paid") or 0) header = BUSINESS_NAME or "ORDER RECEIPT" lines = [ header, "=" * max(len(header), 28), f"Date: {date}", f"Customer: {customer}", "", "Order:", _item_lines(order), ] if order.get("next_appointment"): lines.append(f"\nPickup/Delivery: {order['next_appointment']}") lines += [ "", f"Order total: {money(order.get('amount_charged'))}", f"Paid: {money(paid)}" + (f" ({order['payment_method']})" if order.get("payment_method") else ""), f"Balance: {money(balance)}", ] if balance <= 0: lines.append("\nStatus: PAID IN FULL — thank you!") elif paid > 0: lines.append(f"\nStatus: DEPOSIT PAID — {money(balance)} due on collection") else: lines.append(f"\nStatus: UNPAID — {money(balance)} due on collection") return "\n".join(lines) _HONORIFICS = {"mr", "mrs", "ms", "miss", "madam", "mdm", "auntie", "aunty", "uncle", "dr"} def followup(order: dict) -> str: """Ready-to-send WhatsApp/SMS order confirmation, tone depends on payment.""" name = (order.get("customer") or "").strip() first = name.split()[0] if name else "there" # "Mrs Tan" must not become "Hi Mrs," — keep the full name after an honorific. customer = name if first.lower().rstrip(".") in _HONORIFICS else first balance = _balance(order) paid = float(order.get("amount_paid") or 0) items = order.get("services") or [] item_txt = " and ".join(items).lower() if items else "your order" if balance <= 0: msg = f"Hi {customer}, thanks so much for your order of {item_txt} — all paid, " msg += "and it'll be ready for you." elif paid > 0: msg = (f"Hi {customer}, thanks for your order of {item_txt}! " f"Total {money(order.get('amount_charged'))}, deposit of {money(paid)} received — " f"just {money(balance)} to settle on collection.") else: msg = (f"Hi {customer}, confirming your order of {item_txt}. " f"That's {money(order.get('amount_charged'))}, payable on collection.") if order.get("next_appointment"): msg += f" Ready for {order['next_appointment']}. See you then!" return msg def reminder(order: dict) -> str: customer = order.get("customer") or "Customer" when = order.get("next_appointment") items = ", ".join(order.get("services") or []) or "order" if not when: return "No pickup/delivery time set." return f"🧁 {when}: {customer} — {items}" def supplies_list(order: dict) -> str: items = order.get("supplies") or [] if not items: return "No ingredients to buy." return "🛒 Ingredients to buy:\n" + "\n".join(f" ☐ {i}" for i in items) def generate_all(order: dict) -> dict: """Convenience: all four outputs in one dict, for the UI.""" return { "invoice": invoice(order), "followup": followup(order), "reminder": reminder(order), "supplies": supplies_list(order), }