Group Orders (polls)
Group Orders (polls)
This document outlines the database models and processes that power the Group Order (internally referred to as OrderPoll) feature on the CaterCow marketplace. Group Orders allow a team organizer to select a menu and invite team members to make their own meal selections for a specific event. For how group orders relate to orders, see Orders. For how group orders relate to teams, see Teams. For how group orders relate to menus, see Menus. For how group orders relate to packages, see Packages.
Overview
The Group Order system is designed to bridge the gap between a single organized order and the individual preferences of team members. It starts with an OrderPoll, which defines the available meal choices. Team members then make a PollSelection. Finally, these selections are aggregated and applied to the main Order record as OrderItems or PackageChoiceSelections just before the order is submitted to the restaurant.
Entity Relationship Diagram (ERD)
The following diagram illustrates the relationships between the primary models involved in the Group Order process.
Core Models
The following models are central to the Group Order functionality.
OrderPoll
This is the main model that represents a single group order event. It acts as the central hub, linking the order, the team, and all the associated selections and options.
- Table:
order_polls - Purpose: To manage the settings, options, and participants of a group order.
Key Columns:
id: Primary Key.order_id: A foreign key linking to the parentOrderthat will be sent to the restaurant.team_id: A foreign key to theTeamthat the order belongs to. This is the source for team-specific rules.user_id: The user who organized the group order.code: A unique, shareable code that allows members to access and respond to the poll.package_id: If the poll is for a package, this links to thePackagerecord.package_item_id: Links to the specificPackageItemwithin a package that members are choosing options for (e.g., the "Main Course" item).extra_selections: A JSON field where the organizer can add extra meals for attendees who did not respond. It contains an array of objects, each with aqtyand an identifier likepackage_item_option_idormenu_item_id.budget_limit: A per-person budget or stipend. If a user's selection exceeds this amount, they may be required to pay the difference.auto_submit: A boolean flag indicating whether the system should automatically apply selections and submit the parentOrderwhen the poll expires.combo_creation_allowed: A boolean that determines if users can build their own package combo options, or if they must choose from the ones pre-assembled.applied_selections_at: A timestamp that is set when the poll selections are finalized and copied to the parentOrder. Once this is set, the poll is locked.
PollOption
A PollOption represents a specific, selectable choice within a group order. It is a curated subset of a restaurant's full menu, tailored for the poll.
- Table:
poll_options - Purpose: To define the universe of choices available to participants in a specific group order.
Key Columns:
id: Primary Key.order_poll_id: A foreign key linking back to theOrderPoll.package_item_option_id: If the poll is for a package, this links to a specificPackageItemOption(e.g., "Chicken Teriyaki Box").menu_item_id: If the poll is not package-based (i.e., à la carte), this links directly to aMenuItem.modifiers_json: A JSON copy of the modifier rules for the associated menu item. This ensures that validation rules are locked in for the poll, even if the source menu changes later.price: The base price of this specific option.has_modifiers: A boolean indicating if this option has customizable modifiers.
PollSelection
This model records an individual participant's choice for a group order. Each user or guest can have only one PollSelection per OrderPoll.
- Table:
poll_selections - Purpose: To store the specific item and modifiers chosen by a single participant.
Key Columns:
id: Primary Key.order_poll_id: A foreign key linking to theOrderPoll.user_id: A foreign key to theUserwho made the selection. This can beNULLfor guest responses.anonymous_user_id: A unique ID for a guest who responds without a full user account.name: The name provided by a guest user.package_item_option_id: The selected package option.menu_item_id: The selected menu item (for non-package polls).modifier_selections_encoded: A string representing the user's specific modifier choices for their selected item.opted_out: A boolean flag for users who actively choose not to receive a meal.price: The final calculated price for this selection, including any modifier upcharges.overage: A generated column that calculates the amount by which the selectionpriceexceeds the poll'sbudget_limit.credit_card_id: If an overage must be paid, this links to the user'sCreditCard.
Team
The Team model holds settings and rules that apply to a group of users, often a company.
- Table:
teams - Purpose: To configure rules that govern all group orders for a specific team.
Key Columns:
id: Primary Key.poll_limit_count: The maximum number of polls a team member can respond to within a specific interval (e.g., a limit of 1 per day).poll_limit_interval: The time window for thepoll_limit_count, such asdailyorweekly.allow_anonymous_poll_responses: A boolean that determines if non-account holders (guests) can participate in the team's polls.
Process Flow: From Poll to Order
The lifecycle of a group order involves two key processes: making a selection and applying those selections to a final order.
Making a Selection
When a user or guest responds to a poll, the SelectPollOption service is triggered.
- Validation: Before saving, several conditions are checked:
- The parent
Ordermust not have been submitted yet (order_submitted). - The
OrderPollmust not be expired (poll_expired). - If the poll disallows anonymous responses, a
Usermust be present (anonymity_disabled). - The chosen option must be valid and part of the poll's curated list (
option_package_invalid,option_not_whitelisted).
- The parent
- Team Limit Checks:
- Usage Limit: The system checks if the user has exceeded their
poll_limit_countfor the givenpoll_limit_interval(e.g., placing a lunch order when they already have one for the day). - Budget Limit: If the poll has a
budget_limit(stipend), the system compares the selection's price. If the price is over the limit, the user may need to provide a credit card for theoverage.
- Usage Limit: The system checks if the user has exceeded their
- Saving the Selection: The
PollSelectionrecord is saved with the user's choices, including the selected item and any modifiers. For a user's first selection with a credit card, that card may be saved as theirdefault_poll_credit_card_id.
Applying Selections to the Order
Before the organizer submits the final order to the restaurant, this service runs to translate all the individual poll responses into a format the Order model can use.
- Data Aggregation: The service gathers all non-excluded
PollSelectionrecords and theextra_selectionsJSON from theOrderPoll. - Counting: It counts the total quantity of each unique selection.
- For package-based orders, it counts how many people chose each
package_item_option_id. - For à la carte orders, it counts each unique combination of
menu_item_idandmodifier_selections_encoded.
- For package-based orders, it counts how many people chose each
- Adding Extras: If the total number of selections is less than a required headcount (e.g., an office headcount or a restaurant minimum), the system intelligently distributes extra items. It typically adds to the most popular selected items to meet the minimum.
- Updating the Order: The aggregated counts are used to populate the parent
Order.- For packages,
package_choice_selectionsrecords are created or updated. - For à la carte,
order_itemsare created or updated.
- For packages,
- Locking the Poll: The
applied_selections_attimestamp on theOrderPollis set. This locks the poll, preventing any further changes from users or the organizer and ensuring the final order matches the collected responses.
After this process, the parent Order is fully populated with the correct items and quantities and is ready for submission.