Table Service Integration: models, API, waiter POS views, tests, and documentation#135
Table Service Integration: models, API, waiter POS views, tests, and documentation#135
Conversation
…outes - Create tables and patrons database migrations - Add payment_status, patron_id, table_id to orders table - Add allow_unpaid_table_orders to events table - Create Table and Patron Eloquent models with relationships - Update Order model with new status constants and relationships - Update Event model with new fillable fields and relationships - Create TableResourceDefinition and PatronResourceDefinition - Update OrderResourceDefinition with payment_status, patron_id, table_id - Update EventResourceDefinition in both APIs with allow_unpaid_table_orders - Create shared TableController and PatronController with thin API stubs - Create TablePolicy and PatronPolicy - Register policies in AuthServiceProvider - Register routes in both ManagementApi and DeviceApi - Create PatronAssignmentService for patron lookup/creation algorithm Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
- Create TableService.js and PatronService.js frontend services - Create Tables.vue view for table management (shared between POS and Manage) - Create WaiterDashboard.vue view for waiter POS mode (tables, patrons, order queue) - Create PatronDetail.vue view for patron detail and balance settlement - Register new routes in POS and Manage app.js - Add table service navigation links in Events.vue for both apps - Add allow_unpaid_table_orders checkbox in Manage Events edit modal Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
…e balance Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
|
@copilot add thorough unit and integration tests and documentation. |
PHP Unit Tests (6 new test files, ~70 new tests): - TableModelTest: model attributes, fillable, relationships, soft deletes - PatronModelTest: model attributes, fillable, relationships, methods - OrderStatusConstantsTest: new fulfillment/payment status constants - TablePolicyTest: authorization for user, device, and null (20 tests) - PatronPolicyTest: authorization for user, device, and null (20 tests) - PatronAssignmentServiceTest: service instantiation, constants, edge cases JavaScript/Vitest Tests (3 new test files, 66 new tests): - table-service-routes.test.js: route config in POS/Manage/Client apps - table-service-views.test.js: template content verification for Events.vue - table-service-services.test.js: service structure and shared view integrity Documentation: - .ai/table-service.md: comprehensive architecture docs Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
Added in commit 1a404f4: PHP Unit Tests (6 files, ~70 tests):
Vitest Tests (3 files, 66 tests):
Documentation: All 469 tests pass (203 PHPUnit + 234 Vitest + 32 Jest). Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
1. tables should only be manageable in the manage app, not in the pos2. when settling a debt (in patrondetail), we must follow a similar flow as in LiveSales:=> it would make sense to add a new method '$paymentService.orders(orders: Order[])` that handles the payment of multiple orders in a single go. 3. In the POS 'settings' page, add a setting 'Allow table service at this terminal' that hides / shows the table service features. When selecting 'Allow table service at this terminal', it should not be possible to select any of the other two features (live orders & remote orders) as these are not really compatible with the use case.4. Instead of a separate page, the wait dashboard should be integrated in the Headquarter component5. The 'patron' selection and patron details view should show in modal, not in a seperate page6. A table server must be able to create a new order from the patron details view.Implement these changes. In addition, take a look at the original specs and see if we have all required features: |
…w, settings toggle 1. Tables only manageable in manage app - removed standalone routes from POS 2. Settlement uses PaymentService flow - added $paymentService.orders() for batch payment 3. POS Settings: 'Allow table service' toggle, mutually exclusive with live/remote 4. Waiter dashboard integrated into Headquarters component 5. Patron detail shown as modal within Headquarters 6. New order form in patron modal for creating orders Updated all tests to match new architecture. Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
All files now use tab indentation per project conventions (CLAUDE.md). Updated .ai/table-service.md to reflect the new integrated architecture. Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
... All 6 items implemented in commits
All 485 tests pass (203 PHPUnit + 250 Vitest + 32 Jest). Frontend build succeeds. Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
|
1. Patron selection & details in same modal: table click opens modal at patron list step, selecting patron transitions to details view, "Back to patron list" returns to selection. 2. Table service extracted to pos/js/components/TableService.vue. Headquarters delegates via <table-service> component tag. 3. New order form reuses LiveSales component with patronId, tableId, and allowPayLater props. No more duplicated menu/order code. 4. Pay Later button added to PaymentPopup when allow_pay_later is set on PaymentService. PaymentService.payLater() resolves with paymentType 'pay-later'. LiveSales enables/resets the flag per order. Co-authored-by: daedeloth <1168599+daedeloth@users.noreply.github.com>
All 4 items implemented in commit
All 506 tests pass (203 PHPUnit + 271 Vitest + 32 Jest). Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
Introduces Table Service mode for the POS app — waiters can take orders at tables, track patrons, manage fulfillment/payment states independently, and settle tabs.
Database & Models
Tablemodel — belongs to Event, soft-deletable, unique(event_id, table_number). IncludesbulkGenerate()that increments from highest active table number.Patronmodel — belongs to Event, optionally linked to Table. Tracks outstanding balance across unpaid orders.payment_status(unpaid/paid/voided) alongside existing fulfillmentstatus. New statuses:prepared,delivered. Defaultpayment_status = 'paid'preserves backward compat.allow_unpaid_table_orderson Event — controls whether waiters can open tabs.API
TableController(Shared) — full CRUD +POST /events/{id}/tables/generatefor bulk creationPatronController(Shared) — CRUD withoutstanding_balanceandhas_unpaid_orderscomputed fieldsOrderResourceDefinitionupdated withpayment_status,patron_id,table_id(all filterable)EventResourceDefinitionvariants exposeallow_unpaid_table_orders$allowDevices = truefor POS accessBusiness Logic
PatronAssignmentServiceimplements the patron resolution algorithm:Frontend
Tables.vue(bulk generate, inline rename, delete) available in manage app only; not exposed in POSTableService.vuecomponent (pos/js/components/) — Isolated table service component containing the table grid, patron modal (with multi-step flow: patron selection → patron details), and order queue. Headquarters delegates to this component when table service mode is active.LiveSalescomponent withpatronId,tableId, andallowPayLaterprops, avoiding code duplication. Orders are created with correct patron/table context.PaymentServicehasallow_pay_laterflag andpayLater()method. Whenallow_unpaid_table_ordersis true,PaymentPopupshows a "🕐 Pay later" button alongside cash/card/voucher options. Resolves withpaymentType: 'pay-later', keepingpayment_status: 'unpaid'. Flag is reset after each order.PaymentService.orders()method handles payment for multiple unpaid orders in a single transaction, following the same payment popup flow as LiveSales (cash/card/voucher)Tests
PHP Unit Tests (6 new test files, ~70 tests):
TableModelTest— model attributes, fillable, relationships, soft deletesPatronModelTest— model attributes, fillable, relationships, methodsOrderStatusConstantsTest— new fulfillment/payment status constants and relationshipsTablePolicyTest— 20 tests covering user/device/null authorization for all policy methodsPatronPolicyTest— 20 tests covering user/device/null authorization for all policy methodsPatronAssignmentServiceTest— service instantiation, constants, null/empty edge casesJavaScript/Vitest Tests (3 new test files, 89 tests):
table-service-routes.test.js— 20 tests verifying POS has no standalone table service routes, manage app has all routes, client app has nonetable-service-views.test.js— 26 tests verifying Headquarters integration (TableService component import), patron modal multi-step flow, LiveSales reuse with props, POS Events without standalone links, manage Events with table service links, Settings toggle, and PaymentPopup pay later supporttable-service-services.test.js— 43 tests verifying TableService, PatronService, PaymentService batchorders()method, PaymentService pay later (allow_pay_laterflag,payLater()method), SettingServiceallowTableServicesupport, and shared view integrityAll 506 tests pass (203 PHPUnit + 271 Vitest + 32 Jest).
Documentation
.ai/table-service.md— comprehensive architecture documentation covering database schema, models, patron assignment algorithm, API endpoints, authorization policies, frontend component architecture (TableService.vue, LiveSales reuse, PaymentPopup pay later flow), patron modal multi-step flow, POS device settings, event settings, and offline considerationsOriginal prompt
This section details on the original issue you should resolve
<issue_title>Feature Specification: Table Service Integration</issue_title>
<issue_description># Feature Specification: Table Service Integration
App: drinks.catlab.eu
Role: Waiter-operated POS / Table Management
Status: Ready for Development
1. Objective
Implement a "Table Service" mode for the existing POS application, allowing waiters to take orders directly at tables via their mobile devices. This requires significant refactoring to introduce
TableandPatronmodels, separate fulfillment from payment states, and update the order routing logic between the Bar POS and Waiter POS.2. Database & Model Refactoring
2.1. Tables Model
Hierarchy: Tables belong directly to an
Event. Each event starts with zero tables.Properties:
id,event_id,table_number(integer),name(localized string, e.g., "Table 1").Deletions: Must utilize soft-deletion (
deleted_at).Generation Utility: Need an admin function to bulk-generate X tables.
Logic: It must query the highest current active (non-soft-deleted)
table_numberand increment intelligently to prevent duplicates.Renaming: Single tables must be easily renameable by event admins.
=> If a remote order arrives with an unknown table number, create that table.
2.2. Patron Model
Event. AnOrderbelongsToaPatron.id,event_id,name(optional, primarily used by quiz software/remote name entry).2.3. Order Status Split
The
Ordermodel must be refactored to separate the physical delivery of the drink from the financial settlement of the tab.status):pending->prepared->delivered. (Note: 'processed' is being renamed to 'delivered')payment_status):unpaid,paid,voided.=> Orders from the 'live' sales are always immediately delivered.
2.4 event.allow_unpaid_orders
Create a new field allow_unpaid_table_orders that determines if waiter can open a 'tab'.
3. Core Logic: Patron & Order Assignment
When a new order is received, the system must determine which
Patrongroup owns it based on the following algorithm:nameis provided, search for an existing Patron with that name who has placed an order within the last X hours (duration configurable).allow_unpaid_online_ordersistrueor if some kind of payment system is configured (currently only: a valid card alias is provided)4. User Interfaces & Workflows
4.1. The Bar POS (Remote Orders Interface)
prepared(ordelivered, depending on how the bar treats it locally, but it drops off their active screen).4.2. The Waiter POS (Table Service Mode)
Device Configuration: POS can be toggled into "Table Service" mode.
Dashboard: Displays a list of all tables in the event + a "No Table" option.
Table Interaction:
Clicking a Table shows active Patrons assigned to it.
Includes a 1-click "Create New Patron" button (creates an anonymous Patron linked to the table).
"No Table" Interaction: Shows a list of Patrons not linked to any table. Waiters can create unlinked Patrons here.
Patron View:
Shows outstanding balance (sum of unpaid orders).
Shows history of previous orders.
Provides the interface to place a new order (mirroring the Live Sales layout).
Waiter Order Management Queue:
Shows a list of all
pendingorpreparedorders.Filters: Toggle to show only orders created by this specific POS device; Toggle to show only
preparedorders.Actions: Mark as
delivered, or mark asvoided(error correction).4.3. Checkout & Payment Flow
delivered(or initiates payment), the standard payment dialog appears (Cash, Card, etc.).allow_unpaid_table_ordersis true, the waiter can select "Pay Later", leaving thepayment_statusasunpaidwhile the fulfillment status becomesdelivered.🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.