Package creation
Description / Background
Package creation combines partner self-service package submission, admin review, package editing, and package-list navigation improvements for All You Can Eat (AYCE) and Party Pack (PP) packages.
The feature has three source layers:
- AYCE self-service creation, AI triage, admin approval, partner edit review, and 13-language translation.
- Party Pack self-service creation using the shared submission and approval workflow with Party Pack-specific pricing and validation rules.
- Package creation v2 navigation and form changes, including Add-on listing, section ordering, schedule defaults, cover-image copy, and payment-rule snapshots.
Backend implementation is represented by merged PR #8001. It introduces the package submission and draft-version workflow, validation and normalization services, admin review endpoints, notification workers, serializer fields, and frontend-facing admin screens.
Objectives
- Allow restaurant partners to create AYCE and Party Pack packages from Partner Portal.
- Validate package data, pricing, schedules, menus, payment options, and package-type rules before persistence.
- Create a review submission for every new partner package on Admin Dashboard.
- Route partner package edits through draft versions and admin review.
- Classify safe and significant package edits for review and application.
- Allow admins to review pending submissions, approve, reject, or edit and approve submissions.
- Notify admins about new submissions and partners about approval, rejection, or admin changes.
- Expose submission status, rejection reason, edit capability, and package data required by frontend screens.
- Support AYCE and Party Pack package-specific creation forms and pricing models.
- Snapshot payment rules at package creation so restaurant configuration changes do not mutate existing package behavior.
Scope
In scope
- AYCE package creation.
- Party Pack package creation.
- Partner Portal package listing, creation, and update flows.
- Package submission status:
pending_review,approved, andrejected. - Package submission types:
new_packageandedit_request. - Draft versions for significant edits.
- Admin pending-approval and submission-history screens.
- Admin approval, rejection, and edit-and-approve flows.
- Submission email notifications.
- Package status filters and frontend serializer fields.
- Package Creation v2 navigation and form layout requirements.
- Package-level payment-rule snapshot requirements.
Out of scope or not confirmed by PR #8001
- AI Prompt A, B, C, and D execution implementation.
- 13-language translation worker implementation beyond existing translation integration points.
- Bulk approval or rejection.
- SLA breach notifications beyond SLA status calculation and display.
- Final payment snapshot column name.
- Final timezone policy for v2 start-date defaults.
Location
Partner Portal
- AYCE creation:
partner.hungryhub.com/packages/create/ayce - Party Pack creation:
partner.hungryhub.com/packages/create/party-pack - All Packages: Partner Portal package listing.
Admin
- Package submissions:
/admin/package_submissions
How to find package creation
Partner package creation
- Open Partner Portal.
- Open
Packages. - Select
Create Package. - Select
AYCEorParty Pack. - Complete the package form.
- Save and confirm submission.
Partner package status
- Open Partner Portal.
- Open
View All Packages. - Use package status filters.
- Review
Active,Active Pending Edit,Pending Review,Rejected, orInactivestatus.
Admin package approval
- Open Admin Dashboard.
- Open
Packages. - Select
Package Submissions. - Review
Pending Approval. - Select
Approve,Reject, orView. - Use submission history to inspect approved or rejected submissions.
How to set Package on Partner Portal
- Package navigation contains
View All PackagesandCreate Package. - Create-page section order is:
- Package Basic Info
- Pricing
- Menu for package
- Bookable schedule and period
- Cover Image for Marketing
- Period type defaults to
On-going. - Start date defaults to today in the restaurant timezone when the timezone policy is applied.
- Schedule label is
All day (based on operating hours). + Add Exceptionand+ Add Scheduleappear at the bottom of their respective sections.- Restaurant configuration changes do not change existing package setting.
Submission review rules
- New partner packages create a
new_packagesubmission. - Partner edits create an
edit_requestsubmission when the package is eligible for editing. - A package cannot receive a second pending edit while one submission is already pending.
- Admin-created packages cannot be edited from Partner Portal.
- Rejected submissions remain immutable for the partner flow.
- Admin approval publishes the restaurant package and applies a pending draft version when present.
- Admin rejection requires a rejection reason.
Sequence Diagram / Flow
flowchart LR
partner[Partner Portal] --> form[AYCE or Party Pack form]
form --> sanitize[Package params sanitizer]
sanitize --> validate[Package validator]
validate --> create[Create package service]
create --> package[Package and restaurant package]
create --> submission[Pending package submission]
submission --> admin_email[Admin notification worker]
submission --> queue[Admin pending approval queue]
queue --> review[Admin review]
review --> approve[Approve or edit and approve]
review --> reject[Reject with reason]
approve --> draft[Apply pending draft version]
draft --> publish[Activate and show package]
approve --> partner_email[Partner approval notification]
reject --> partner_reject[Partner rejection notification]
partner --> update[Update active partner package]
update --> detect[Package change detector]
detect --> draft_request[Draft version and edit request]
draft_request --> queue
ERD
erDiagram
RESTAURANT ||--o{ RESTAURANT_PACKAGE : assigns
RESTAURANT ||--o{ PACKAGE_SUBMISSION : receives
STAFF ||--o{ PACKAGE_SUBMISSION : submits
USER ||--o{ PACKAGE_SUBMISSION : reviews
PACKAGE ||--o{ PACKAGE_SUBMISSION : has
RESTAURANT_PACKAGE ||--o{ PACKAGE_SUBMISSION : references
PACKAGE ||--o{ PACKAGE_DRAFT_VERSION : versions
PACKAGE_SUBMISSION ||--o| PACKAGE_DRAFT_VERSION : contains
PACKAGE_SUBMISSION {
bigint id
bigint package_id
string package_type
bigint restaurant_id
bigint staff_id
string submission_type
string approval_status
string package_category
bigint reviewed_by_id
datetime reviewed_at
text rejection_reason
text admin_notes
datetime submitted_at
bigint draft_version_id
bigint restaurant_package_id
}
PACKAGE_DRAFT_VERSION {
bigint id
bigint package_id
string package_type
json previous_state
json proposed_state
json changed_fields
string status
datetime applied_at
}
RESTAURANT_PACKAGE {
bigint id
bigint restaurant_id
bigint package_id
boolean active
boolean is_visible
}
Backend Implementation
-
POST /api/partner/v1/restaurant_packages- Creates AYCE or Party Pack package through
CreatePackageService. - Returns
restaurant_package_idandsubmission_idon success.
- Creates AYCE or Party Pack package through
-
PATCH /api/partner/v1/restaurant_packages/:id- Updates eligible partner-created active packages through
UpdatePackageService. - Creates a draft version and edit-request submission.
- Updates eligible partner-created active packages through
-
GET /api/partner/v1/package_submissions/:id- Returns submission identity, review metadata, SLA status, and restaurant-package payload.
-
PATCH /api/partner/v1/package_submissions/:id/approve- Requires
partner-portal-adminOAuth token withpartner-adminscope. - Approves a submission with no package payload, with draft data, or with admin-edited package data.
- Requires
-
PATCH /api/partner/v1/package_submissions/:id/reject- Requires
rejection_reason. - Returns
400when the reason is blank.
- Requires
-
GET /api/partner/v1/restaurant_packages/payment_types- Returns payment types filtered by restaurant currency.
-
GET /admin/package_submissions- Renders pending submissions and approval history.
-
PATCH /admin/package_submissions/:id/approve- Approves from the Rails admin flow and redirects to the submissions page.
-
PATCH /admin/package_submissions/:id/reject- Rejects with a form-encoded reason and redirects to the submissions page.
-
Centralizes permitted package attributes and nested-attribute normalization.
-
Handles translation fallbacks, agendas, restaurant-package dates, pricing, kids prices, payment types, deletion flags, package-type fields, and currency conversion.
-
Converts Baht values to Satang when
convert_currency: true. -
Supports existing nested IDs and replacement of nested collections during update and approval.
-
Enforces commission, country, menu type, earn-point, reservation duration, advance-booking time, pricing model, currency, seating, kids-price eligibility, and inactive restaurant-package defaults.
-
Uses
per_personfor AYCE andper_packfor Party Pack. -
Uses inventory maximum, restaurant largest table, or a fallback maximum for max seats.
-
Allows edits only for active packages created through Partner Portal.
-
Rejects admin-created packages.
-
Rejects packages with an existing pending submission.
-
Serializes the previous package state.
-
Detects changed fields.
-
Creates
HhPackage::PackageDraftVersionandedit_requestsubmission. -
Enqueues the admin notification worker.
-
Admin::PackageSubmissionsControllerrenders pending and history grids. -
Pending grid displays SLA badge, submission date, package, type, recommendation, and actions.
-
History grid displays approval status, reviewer date, and expandable rejection reason.
-
Approval applies draft state, activates the restaurant package, and queues partner notification.
-
Rejection records reason and queues partner notification.
-
Partner Portal admin OAuth access token is generated for embedded review.
-
The review endpoint checks OAuth scope and restaurant ownership.
-
Added admin Package Submissions navigation.
-
Added pending submission count and approval-history UI.
-
Added SLA status badges and urgency ordering.
-
Added Approve, Reject, and View actions.
-
Added rejection confirmation modal and required reason validation.
-
Added embedded Partner Portal review iframe with OAuth token and restaurant scope.
-
Added review postMessage handling and automatic list refresh.
-
Added approved/rejected history badges and expandable rejection reasons.
-
Added package submission fields to serializer response:
submission_status,submission_id,reject_reason, andcan_edit. -
Added frontend data fields for cover images, package menus, payment labels, charge policies, reservation duration, pricing, and kids policy.
-
Added package status filters, including
pending_review,rejected, andactive_pending_edit. -
Pull request: https://github.com/hungryhub-team/hh-server/pull/8001
Frontend Implementation
Partner package creation form
- Add AYCE and Party Pack package-type entry points.
- Render common package fields for names, descriptions, images, menus, schedules, seating, pricing, payment, and charge policy.
- Render AYCE-specific price tiers and kids-price controls.
- Render Party Pack-specific dish/course count and per-pack pricing controls.
- Apply bilingual input handling for English and Thai fields.
- Apply image upload constraints and menu-image ordering.
- Apply
Accept Allparent-child behavior for payment methods. - Show contextual prepayment controls and clear dependent values when payment mode changes.
- Display default advance-booking, reservation-duration, seating, and commission behavior.
- Submit package data and retain returned
submission_id.
Package Creation v2 navigation and form updates
- Remove Add-on from the left navigation.
- Include Add-ons in All Packages.
- Display
Add-onin the Package Type column. - Hide Add-on edit actions.
- Block direct Add-on edit routes with
Add-ons cannot be edited yet. - Reorder create-page sections to Basic Info, Pricing, Menu, Schedule, and Cover Image.
- Rename the cover section to
Cover Image for Marketing (Optional). - Default schedule period to
On-going. - Default start date to today under the selected timezone policy.
- Rename the all-day checkbox to
All day (based on operating hours). - Move
+ Add Exceptionand+ Add Scheduleto the bottom of their sections. - Remove Payment Info from Partner Portal.
- Display payment behavior from the package-level snapshot after creation.
Package listing and status UI
- Include Add-ons in All Packages.
- Support status filters for Active, Active Pending Edit, Pending Review, Rejected, and Inactive.
- Display rejection reason for rejected submissions.
- Use
can_editto control partner edit actions. - Keep rejected partner submissions immutable.
- Show pending review state after create or significant edit submission.
Mobile Implementation
N/A. Package creation and admin review scope described by the PRD and PR #8001 is Partner Portal and Admin web implementation.
PRD & Task
- AYCE Package Creation PRD: https://app.clickup.com/9003122396/v/dc/8ca1fpw-30456/8ca1fpw-60936
- Party Pack Package Creation PRD: https://app.clickup.com/9003122396/v/dc/8ca1fpw-30456/8ca1fpw-61396
- Package Creation v2 PRD: https://app.clickup.com/9003122396/v/dc/8ca1fpw-30456/8ca1fpw-63616 Task:
- Package submission backend implementation: https://github.com/hungryhub-team/hh-server/pull/8001
- Draft override email reference: https://app.clickup.com/9003122396/v/dc/8ca1fpw-30456/8ca1fpw-61316
Design
- AYCE and Party Pack Partner Portal design: https://www.figma.com/design/ymdLN2ZKzfI0oyX4kps2GQ/Partner-Portal-Desktop?node-id=17560-25083&t=vcnOJrUoX7l6eXQy-1
- Party Pack Partner Portal design: https://www.figma.com/design/ymdLN2ZKzfI0oyX4kps2GQ/Partner-Portal-Desktop?node-id=17567-40098&t=2Snmy94oAl1r1qcE-1
- Package Creation v2 design: https://www.figma.com/design/ymdLN2ZKzfI0oyX4kps2GQ/Partner-Portal-Desktop?node-id=18341-25362&m=dev
API Blueprint
| Method | Path | URL | Description | Payload |
|---|---|---|---|---|
| POST | /api/partner/v1/restaurant_packages | Partner API base URL | Create AYCE or Party Pack package and return submission ID. | package_type, package, nested agendas, restaurant packages, package attributes, menus, pricing, payment types, and kids prices. |
| PATCH | /api/partner/v1/restaurant_packages/:id | Partner API base URL | Submit update for active partner-created package. | Package fields and nested attributes. Creates draft version and edit request. |
| GET | /api/partner/v1/package_submissions/:id | Partner API base URL | Load submission and review payload. | restaurant_id for scoped admin review. |
| PATCH | /api/partner/v1/package_submissions/:id/approve | Partner API base URL | Approve submission with draft state or final admin edits. | OAuth token, restaurant_id, optional package attributes. |
| PATCH | /api/partner/v1/package_submissions/:id/reject | Partner API base URL | Reject submission and notify partner. | OAuth token, restaurant_id, rejection_reason. |
| GET | /api/partner/v1/restaurant_packages/payment_types | Partner API base URL | Return payment methods allowed for restaurant currency. | Restaurant context. |
| GET | /admin/package_submissions | Admin application | Render pending approvals and submission history. | Admin session and optional pagination params. |
| PATCH | /admin/package_submissions/:id/approve | Admin application | Approve from Rails admin UI. | Form submission with submission ID. |
| PATCH | /admin/package_submissions/:id/reject | Admin application | Reject from Rails admin UI. | rejection_reason. |
Approval and rejection statuses
200: approval or rejection completed.201: package created and submitted.400: invalid package type, invalid payload, missing rejection reason, or invalid state transition.403: missing admin OAuth, invalid scope, or restaurant mismatch.404: submission or package not found.422: package validation failure during approval.500: unexpected approval or rejection failure.
New Query
Pending submissions
HhPackage::PackageSubmission.pending_review.by_urgency
Submission status for restaurant package
HhPackage::PackageSubmission.status_for_restaurant_package(restaurant_package)
Returns one of:
activeactive_pending_editpending_reviewrejectedinactive
Include inactive packages
Api::Partner::RestaurantPackagesFilter.new(restaurants, scope: 'all')
Package edit eligibility
package.active? &&
HhPackage::PackageSubmission.package_created_by_partner?(package) &&
!HhPackage::PackageSubmission.latest_for_package(package)&.pending_review?
DB Schema / Database Migration
hh_package_package_submissions
| Column | Type | Description |
|---|---|---|
package_id | bigint | Polymorphic package ID. |
package_type | varchar(100) | Polymorphic package type. |
restaurant_id | bigint | Restaurant submitting or owning package. |
staff_id | bigint | Partner staff submitter. |
submission_type | varchar(20) | new_package or edit_request. |
approval_status | varchar(20) | pending_review, approved, or rejected. |
package_category | varchar(20) | ayce or pp. |
reviewed_by_id | bigint | Admin reviewer. |
reviewed_at | datetime | Review completion time. |
rejection_reason | text | Required for rejected submissions. |
admin_notes | text | Admin or AI recommendation notes. |
submitted_at | datetime | Submission timestamp. |
draft_version_id | bigint | Associated draft version. |
restaurant_package_id | bigint | Associated restaurant package. |
lock_version | varchar | Optimistic-locking value. |
deleted_at | datetime | Soft-delete timestamp. |
Migration: 20260420051502_create_hh_package_package_submissions.rb
hh_package_package_draft_versions
| Column | Type | Description |
|---|---|---|
package_id | bigint | Polymorphic package ID. |
package_type | varchar | Polymorphic package type. |
previous_state | JSON | Package state before partner edit. |
proposed_state | JSON | Package state submitted for review. |
changed_fields | JSON | Detected changed fields. |
status | varchar(20) | pending, approved, rejected, or invalidated. |
applied_at | datetime | Time draft was applied. |
deleted_at | datetime | Soft-delete timestamp. |
Migration: 20260420051514_create_hh_package_package_draft_versions.rb
Package Creation v2 payment snapshot
The v2 PRD requires a package-level payment snapshot:
accepts_vouchers=truedisplaysOption to Pay Now.accepts_vouchers=falsedisplaysPay On-Site Only.- Snapshot is created once at package creation.
- Partner edits do not re-snapshot.
- Restaurant payment-setting changes do not retroactively mutate snapshots.
- Admin override must be supported.
- Missing restaurant configuration falls back to
Pay On-Site Onlyand logs an internal alert. - Final snapshot field name is not specified in the PRD.
Improvement:
| Feature Name | Date | What Changed | Description |
|---|---|---|---|
| Package self-service creation | 2026-05-15 | Added AYCE and Party Pack partner submission flow | Partner package creation now persists a package submission and returns submission_id. |
| Package edit review | 2026-05-15 | Added draft versions and change detection | Significant package edits are stored as proposed state and reviewed by admins. |
| Admin package approval | 2026-05-15 | Added pending and history review screens | Admins can approve, reject, or open embedded package review. |
| Submission notifications | 2026-05-15 | Added admin and partner workers/mailers | New submissions notify admins; approvals, rejections, and admin edits notify partners. |
| Frontend package status | 2026-05-15 | Added serializer status fields and filters | Frontend can display submission state, rejection reason, submission ID, and edit capability. |
| Package Creation v2 navigation | 2026-05-22 | Moved Add-ons into All Packages | Add-ons appear in package listing and cannot be edited yet. |
| Package Creation v2 form | 2026-05-22 | Reordered sections and simplified schedule controls | Basic info, pricing, menu, schedule, and marketing cover image follow the new order. |
| Package Creation v2 payment behavior | 2026-05-22 | Replaced editable Payment Info with snapshot behavior | Payment behavior is captured at package creation and remains stable for existing packages. |
| PRD/backend commission value difference | 2026-05-15 | Documented implementation discrepancy | AYCE PRD specifies commission default 12.00; PackageDefaultsEnforcer in PR #8001 applies 12.50. |
| PRD/backend AI triage boundary | 2026-05-15 | Documented workflow boundary | PRD specifies AI triage outcomes; PR #8001 provides submission/review infrastructure but does not show AI prompt execution. |