Odoo Restaurant Waiter App & Kitchen Display (KDS) documentation
A guide to the Odoo Restaurant Waiter App & Kitchen Display: installing the module, floors, menu, modifiers, stations, recipes, billing, the waiter app, the kitchen display, real time on the LAN, printing, reports and building the app.
Documents version 1.0.0Updated Oct 29, 2026
Start here
In plain words: This package gives you a complete table-side ordering and kitchen display system for Odoo Community: an Odoo module (
taqnix_restaurant_kds) that extends the restaurant point of sale, and the Flutter source code of one app with two modes: Waiter (phones and small tablets) and Kitchen Display / KDS (10 to 15 inch kitchen tablets). Waiters take orders at the table, the kitchen sees one ticket per station a fraction of a second later, and when the guests pay, Odoo books the sale in the open POS session and takes the ingredients out of the stock.Tip: Nothing here needs Odoo Enterprise. The module uses only Community apps: Point of Sale with its restaurant extension, Manufacturing (for the recipes), Inventory and Invoicing. The live link between the devices is the websocket that every Odoo already has.
What is in the package
| Item | What it is |
|---|---|
taqnix_restaurant_kds.zip | The Odoo 19 module: floors and tables with live states, orders, courses with hold and fire, stations, tickets and SLA timers, modifiers, bills and splits, payments into the POS session, recipe consumption, reports, the API, Arabic translation and automated tests. |
flutter-app-source.zip | The Flutter project (Android and iOS): the waiter mode and the KDS mode in one code base, offline outbox, real-time client with catch-up, ESC/POS printing over Bluetooth and over the network (port 9100), setup script, tests. |
documentation/ | This guide in English (index.html) and Arabic (index-ar.html), with PDFs, and API.md for developers. |
licensing/ | The licence (OPL-1: one purchase = one company and one app brand, updates included). |
preview-images/ | Screenshots of the apps and of the Odoo screens. |
changelog.txt | History of the versions. |
How a service works
| Moment | Who | What happens | In Odoo |
|---|---|---|---|
| Seating | Waiter | Taps a grey table on the floor map and enters the number of guests. | An order opens; the table turns blue. |
| Ordering | Waiter | Picks dishes, chooses the forced options (meat temperature), the paid add-ons and writes the kitchen note. Appetizers and drinks are sent at once, mains and desserts stay on hold. | Lines with course, seat, options and prices from the menu. |
| Kitchen | Kitchen | A ticket appears on every station that has an item, with a chime. The timer runs green, orange, red. A tap on Bump says it is ready. | One ticket per station; the table turns yellow; ingredients are taken from the stock. |
| Pass | Waiter | Gets the notification, serves, and later fires the mains when the appetizers are cleared. | Lines become served; the table turns green. |
| Payment | Waiter | Asks for the bill, splits it, takes cash, card or a QR payment, adds the tip, prints the receipt. | The table turns red, then grey again; a POS order (and an invoice on request) is booked. |
| Back office | Manager | Watches the floor map, the sales by waiter, the preparation times and the ingredient use. | Reports and the normal POS accounting. |
In plain words: Zero-latency on your own network. The devices talk to your Odoo server through the Odoo websocket. When Odoo runs on a small computer in the restaurant, the ticket is on the kitchen screen in a few milliseconds and nothing depends on the internet (chapter 21). If the Wi-Fi blinks, the waiter app keeps the order on the device and sends it when the link is back.
What you need
| Item | Details |
|---|---|
| Odoo | 19.0 Community or Enterprise. The apps Point of Sale with Restaurant, Manufacturing, Inventory and Invoicing are installed automatically with the module. |
| Chart of accounts | A chart of accounts with a cash journal and a bank journal (any localization). Taxes of the kind percentage are supported; tax-included prices work. |
| Server | For one restaurant: any small computer (4 GB RAM, SSD) at the restaurant, or a cloud server if you accept that the internet is part of the service. See chapter 21. |
| Network | A good Wi-Fi with all devices on the same network as the server. A fixed IP address for the server. |
| Devices | Waiters: Android 8+ phones or iPhones (iOS 15+). Kitchen: tablets of 10 to 15 inch, wall or stand mounted, always powered. |
| Printers | Optional: ESC/POS thermal printers of 58 or 80 mm, Bluetooth or on the network (TCP port 9100). |
| A computer to build the app | A Mac for iPhone and Android (Windows or Linux can build Android only), about 30 GB free, Flutter 3.41 or newer, Android Studio and Xcode. |
| Store accounts | Google Play Console and Apple Developer Program, or internal distribution to your own devices. |
Install the Odoo module
- Unzip the package. Inside is
taqnix_restaurant_kds.zip; unzip it to get the foldertaqnix_restaurant_kds. - Copy the folder into one of the
addons_pathfolders of your Odoo server and restart Odoo. - Log in as an administrator, activate Developer mode, open Apps and press Update Apps List.
- Remove the “Apps” filter, search Restaurant Waiter App and press Install.
# or from the command line ./odoo-bin -c odoo.conf -d YOUR_DATABASE -i taqnix_restaurant_kds --stop-after-init
In plain words: After the installation a new app Restaurant appears in the main menu. The installation also creates the four courses (Beverage, Appetizer, Main and Dessert; the last two on hold by default) and a list of void reasons. Everything can be changed.
Important: Take a backup before installing on a production database, as for any module. The dish products must be not tracked in stock (the ingredients are); otherwise the point of sale also takes the dish out of the stock when the session closes.
First setup in 20 minutes
- Open Restaurant → Configuration → Restaurants and open your restaurant (a point of sale that is a restaurant). Tick Waiter app and kitchen display, add the payment methods and the floors.
- Create the floors and tables (chapter 7) and arrange them in the floor map editor.
- Put your dishes in the point of sale (Available in POS) with their menu category; create the stations and tell each station which categories it prepares (chapters 8 and 10).
- Add modifier groups to the dishes that need them (chapter 9).
- Give the dishes a bill of materials and choose the kitchen and bar stock locations (chapter 11).
- Create the users: waiters, kitchen users with their station and a manager (chapter 13).
- Open a POS session for the restaurant (the cashier or manager does it in Point of Sale → Dashboard), so payments can be booked.
- Build the app with the setup script (chapter 24), install it on the devices, sign in and take a test order.
Tip: The demo data of the screenshots (The Copper Grill) is created by
tests/seed.py in the source package: it is a complete example of every setting.Floors, tables and the floor map
In plain words: Floors and tables are the ones of the Odoo restaurant point of sale, so the standard POS screen shows the same plan. This module adds the zone of a floor (Dining, Patio, Bar, VIP), a short name for the apps (Main 4) and a third shape, the long banquet.
| Colour | State | Meaning |
|---|---|---|
| Grey | Vacant | No open order. |
| Blue | Seated / ordering | Guests are seated and the order is being taken, or only held items exist. |
| Yellow | Kitchen in progress | At least one item is in the kitchen or ready but not yet served. |
| Green | Served | Everything that was sent has been served. |
| Red | Bill requested | The bill was asked; the table is waiting for payment. |
In plain words: Layout editor. Managers press Edit layout on the floor map: drag a table to move it (it snaps to a grid), click it to change the number, seats, shape and size, Add table for a new one and Remove table to archive one that has no open order. The waiter app shows the same plan at once.
Modifiers: forced options, add-ons and notes
In plain words: A modifier group is a set of choices. Forced groups (meat temperature: Rare, Medium rare, Well done) must be answered before the dish can be added. Optional groups carry an upcharge (Extra cheese +1.50) that is added to the price and taxed like the dish. A group can allow one choice or several, with a minimum and a maximum.
In plain words: An option can name an ingredient and a quantity: choosing Extra cheese then also takes one cheese slice out of the stock. The waiter also types a free kitchen note (for example Severe peanut allergy, Dressing on the side); the note is shown in a highlighted box on the ticket, and the app offers quick chips for the common ones.
Stations and smart routing
In plain words: A station is a screen: Grill, Fryer, Cold Prep, Bar. One order is split into one ticket per station automatically. The Expo station is special: it sees the tickets of every station.
| Rule (in this order) | Example |
|---|---|
| The dish names a prep station | Calamari → Fryer |
| A menu (POS) category or product category of the dish belongs to a station | From the Grill, Burgers, Pasta → Grill; Cocktails, Wine → Bar |
| The default station of the restaurant | Anything else → Grill |
| No station found and no default | The dish is not sent to a screen |
In plain words: Timers. Each ticket counts from the moment it was sent: green until the warning time (10 minutes), orange until the late time (15), then flashing red. The times are set per restaurant and can be overridden per station (a salad bar is faster than a steak grill). The first time a ticket is late is stored for the reports.
Recipes and stock consumption
In plain words: Give a dish a normal bill of materials in Inventory/Manufacturing and the module takes the ingredients out of the stock for you. Multi-level recipes work: a burger can use a house sauce that has its own bill of materials; with All levels the sauce is opened and mayonnaise and ketchup are consumed, with Kit recipes only the sauce itself is consumed.
| Setting Consume ingredients | When the stock moves |
|---|---|
| When the item is ready (bumped) | The moment the kitchen bumps it (default). A recalled ticket puts the ingredients back and takes them again at the next bump. |
| When it is sent to the kitchen | At the moment the ticket is created. |
| When the bill is paid | At the end, for everything that was served. |
| Never | No stock movement. |
In plain words: Where from. Each station can have its own stock location (the bar takes the liquor from the Bar location); the others use the Kitchen stock location of the restaurant. Stock moves go from there to the virtual production location.
In plain words: Waste. An item that is voided after it was made keeps its ingredients consumed and is marked as waste, so the report shows what was lost.
Bills, payments, tips and invoices
In plain words: Paying works on bills. Asking for the bill creates one bill with everything on it; the waiter can split it. Every payment is collected in the apps; when the last bill is paid, the module books the sale in the open POS session of the restaurant (a normal POS order with its payments, so your POS reports and closing work as usual).
| Split | How it works | Booked in the POS as |
|---|---|---|
| None | One bill for the table. | One order. |
| Equal parts | Divide by the number of guests; the last guest takes the rounding cent. | One order with one payment per guest. |
| By item or seat | Each item (or a share of it, for a dish that the table shares) is given to a guest. | One order per guest. |
| By amount | Any amounts that add up to the total. | One order with the payments. |
| Topic | Details |
|---|---|
| Payment methods | The methods of the point of sale. Cash accepts more than the amount and shows the change. Mark a method Show a QR code in the waiter app to show the QR of the bill. |
| QR payment | In the restaurant form, QR payment text is the text of the QR with {amount}, {currency}, {reference} and {company}, for example a UPI link. Odoo cannot know that the guest paid by QR: the waiter confirms. |
| Tips | Enable Product tips in the restaurant form (and choose the tip product). The waiter picks a percentage or types an amount; the tip becomes a line of the POS order. |
| Invoice | At payment the waiter ticks Invoice and types the customer's name and tax number. Odoo creates or finds the customer and posts the invoice. |
| No open session | The app says Open a point-of-sale session. Open the session and press the button again. |
Important: Dishes must not be stored products: the point of sale takes sold products out of the stock again when the session closes. The ingredients are consumed by this module.
Users, roles and devices
| Group (Restaurant) | Can |
|---|---|
| Waiter | Sign in to the waiter mode, take orders, serve, ask and split the bill, collect payments (if allowed), mark an item sold out. In Odoo the waiter sees only their own orders. |
| Kitchen | Sign in to the KDS mode, see tickets, bump, recall and rush. Add stations in the user's Restaurant tab to limit the screens the user can open; empty means all. |
| Manager | Everything: voids prepared items, cancels orders, edits the setup, sees the reports. Implies Point of Sale manager rights. |
In plain words: Every phone or tablet that signs in appears in Configuration → Devices with its app version and the last time it was seen. Delete a device to sign it out. Accounts with two-factor authentication cannot use the apps.
Restaurant settings reference
| Setting | Meaning |
|---|---|
| Guests by default / Maximum | The guest stepper of the app starts at the default and stops at the maximum. |
| Hold and fire | Switch off if you never hold courses: everything is sent at once. |
| Merge tables / Split bills | Switch the features off for restaurants that do not need them. |
| Waiters collect payments | Off: only managers can take payments in the apps. |
| Waiters can mark sold out (86) | Off: only the kitchen and managers can. |
| Void after ready | Who can void an item that is already ready or served: only managers, waiters too, or nobody. |
| Stations / Default station | The screens of the restaurant and the one that gets dishes without a rule. |
| Warning / Late after (min) | The timer colours. |
| Consume ingredients, Recipe levels, Kitchen stock location | Chapter 11. |
| Product tips, tip choices | Chapter 12. |
| Receipt paper, print mode, copies, header, footer | 58 or 80 mm; Image prints any language, the logo and the QR; Text is the fastest for Latin text. |
The waiter app
In plain words: Sign in with the server address, your login and password. The app opens on the floor map: floors as tabs, every table in its live colour with the number of guests, the running total and the time since seating; a table pulses when food is ready.
- Tap a grey table, set the number of guests, press Seat guests.
- Pick a category and tap a dish; use the search for a quick find. Sold-out dishes are greyed.
- In the sheet that opens choose the forced options, the add-ons, the seat and the course, type a kitchen note, then Add.
- Review the items in the cart, then press Send to kitchen. Held items stay on hold; everything else goes to the stations at once.
Courses: hold and fire
In plain words: Dishes of a course on hold (mains and desserts by default) are saved but not sent. When the appetizers are cleared, press Fire on the course: the items go to their stations and the tickets appear. Fire all sends every held course, and Hold takes items that nobody started back.
Tip: Change the course of one dish before sending it, or switch Hold on or off for it in the add sheet. The bill cannot be asked while items are still on hold: fire or void them first.
Transfer, merge, void, rush and 86
| Action | How | Result |
|---|---|---|
| Transfer | Table sheet → Transfer → pick a vacant table. | The party and the order move; the tickets show the new table. |
| Merge | Table sheet → Merge → pick the other table (occupied or not). | The tables share one order; both show the same colour. Unmerge when the party leaves. |
| Void | Long-press an item → Void → choose a reason. | The kitchen hears an alert and sees the item struck through. Items already ready can only be voided by managers (setting). |
| Rush | Order sheet → Rush. | Tickets of the order show a red RUSH tag and sort first. |
| Sold out (86) | Menu → long-press a dish → Sold out. | Every device greys the dish. |
Splitting and paying the bill
In plain words: Ask for the bill: the table turns red and the cashiers are told. The split sheet offers three ways: evenly between guests, by item or seat (the app starts from the seat numbers of the items, you can give a share of a shared dish to several guests), and custom amounts. The sheet shows the total of each guest and warns if an item is not fully assigned.
In plain words: For each guest choose the payment: cash (the keypad and quick amounts show the change), card or other methods, or a QR code that shows the amount to scan. Add a tip, optionally the customer for an invoice, and press Pay. Then print the receipt or share it.
The kitchen display (KDS mode)
In plain words: The KDS mode is made for a wall-mounted tablet: landscape, always awake, large text, dark theme. Choose the station at the top (Grill, Cold Prep, Bar or Expo / All); the choice is remembered on the device.
| On the card | What it shows |
|---|---|
| Header | Ticket number, table and floor, waiter, guests, a RUSH tag, an add-on tag when it is a second round. |
| Timer | Green, orange and flashing red (with an icon, not only colour) from the moment the ticket was sent. |
| Items | Quantity and name, the options in bold, the kitchen note in a highlighted box (allergies in red), the course. |
| Bump | One big button marks the whole ticket ready. A tap on an item marks only that item. |
In plain words: Sounds. A chime plays for every new ticket and a different alert when an item is voided (the item is struck through in red). The sound can be muted in the settings.
All-day count, recall and the expo view
In plain words: The banner at the top is the all-day / active item count: identical items with identical options added up over all active tickets, for example 8 × Ribeye (Medium rare) across 4 tables. A tap opens the full list, with a switch for the whole day.
In plain words: Recall. A ticket bumped by mistake is in the Undo / Recall drawer for 30 minutes; one tap brings it back with its original time. Expo. The expo view shows every station's tickets grouped by table, with a chip per station, so the expediter sees when the whole table is complete.
Real time on your own network
In plain words: Every change (a new ticket, a bump, a void, a table colour) is written to an event log in Odoo and pushed on the Odoo websocket on a secret channel of the restaurant. All devices subscribe to it. Measured on a development laptop, a ticket appears on the websocket 2 to 4 ms after Odoo answered the waiter, about 80 ms from the tap to the screen including the HTTP request; on a real LAN count on well under 300 ms. The numbers depend on your Wi-Fi and server.
| Situation | What happens |
|---|---|
| The websocket drops | The kitchen board keeps the last tickets, timers keep running, a banner says reconnecting. When it is back the app asks for the events it missed (/events?since=); if the log no longer has them it reloads everything. |
| The server cannot be reached by a waiter | The orders are kept in the outbox of the phone (with an unchangeable sequence number and a unique ID) and sent in order when the link returns; the table shows not sent yet. |
| The internet is down | Nothing changes if Odoo runs in the restaurant: the devices only need the local network. If Odoo is in the cloud, the service stops, so use a local server for the live service. |
| Websocket blocked by a proxy | The app falls back to polling the event log every 15 seconds. |
| Two waiters send to one table | Both orders are added to the same open order. |
In plain words: Installing the server on the LAN. Run Odoo and PostgreSQL on a small computer in the restaurant, give it a fixed IP address (for example
192.168.1.10), use the Odoo address http://192.168.1.10:8069 in the app, and keep the cloud for nightly backups. With workers = 0 (default for small installs) the websocket is on the same port. With workers, run the gevent port (gevent_port = 8072) and route /websocket to it:location /websocket {
proxy_pass http://127.0.0.1:8072;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 3600s;
}Important: Plain
http on a local network is allowed by the app builds only when you enable it (the setup script has an option; the trade-off is explained in the app README). Use https whenever the server is on the internet.Printing receipts
In plain words: Receipts are printed by the app: no printing hardware needs to be attached to Odoo. The app generates the ESC/POS commands itself and sends them over Bluetooth (Android: Bluetooth Classic printers; iPhone: only printers with Bluetooth Low Energy) or over the network (TCP, usually port 9100). Pair the printer in Settings → Printer; the test print checks the width (58 or 80 mm).
In plain words: Print mode. Image (default) draws the receipt as a picture at the printer's width, so Arabic, the logo and the QR code print correctly; Text sends plain ESC/POS text, the fastest for Latin text. A pro-forma bill can be printed before payment, and the receipt can be shared as PDF or image. The same receipt exists in Odoo as an 80 mm report on every bill.
Reports
In plain words: Restaurant → Reporting has three analyses, each with pivot, graph and list views and the usual filters and groupings:
In plain words: Open tables is a kanban of the live dining room by table state, Kitchen tickets lists every ticket with its preparation time and SLA state, and Bills lists every bill with its payments, tip and invoice.
Build and publish the apps
In plain words: Unzip
flutter-app-source.zip. The README in the folder is the full guide; the short version:- Install Flutter 3.41 or newer (
flutter doctormust be green for your platforms). - Run the setup script with your server and your package name, and optionally the mode to lock the device to:
dart run tools/setup.dart --url http://192.168.1.10:8069 --package com.yourrestaurant.tableservice
- Replace the placeholder icon (
assets/branding), runflutter pub get. - Android:
flutter build apk --release(orappbundlefor Google Play). iPhone and iPad:flutter build ios --release, then archive in Xcode. - Install the waiter build on the phones and the same app on the kitchen tablets: the mode follows the user's role, or lock a tablet to KDS with the setup script.
- Kitchen tablets: use the device's guided access / kiosk mode so the app cannot be closed, and keep them powered.
Important: The app can be built without any store account for your own devices (a debug or ad-hoc build). The stores are only needed to distribute to many devices easily.
API for developers
In plain words: Everything the apps do is available over a small REST API (
/api/kds/v1) plus the live websocket. The contract is in documentation/API.md. Operations are idempotent; a bump or a payment sent twice has the effect of one.# is the server reachable?
curl http://192.168.1.10:8069/api/kds/v1/ping
# {"ok": true, "api_version": 1, "server_time": "2026-10-27T18:30:00Z"}
# sign in (returns a token and the restaurants of the account)
curl -X POST http://192.168.1.10:8069/api/kds/v1/auth/login -H 'Content-Type: application/json' \
-d '{"login": "maya@coppergrill.example", "password": "...", "device": {"id": "tablet-1", "platform": "ios", "mode": "waiter"}}'
# send an order (idempotent: the same operation uuid is applied once)
curl -X POST http://192.168.1.10:8069/api/kds/v1/op/order_push -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"uuid": "4c1f...", "order_uuid": "a9d2...", "table_id": 12, "guests": 4, "fire": true,
"lines": [{"uuid": "7b0e...", "product_id": 82, "qty": 2, "option_ids": [31], "note": "Severe peanut allergy"}]}'
# watch the kitchen events live (Odoo websocket; send an Origin header)
# ws://192.168.1.10:8069/websocket then {"event_name":"subscribe","data":{"channels":["kds.1.9f3a..."],"last":0}}| Call | Use |
|---|---|
POST /auth/login | Token, user roles and restaurants. |
GET /bootstrap | Menu, modifiers, courses, stations, floors, open orders, real-time settings. |
POST /op/<type>, POST /sync | One operation or an ordered batch of up to 100 (order_push, course_fire, ticket_bump, bill_pay, ...). |
GET /tickets, /summary, /tickets/history | The board of a station, the all-day count and the recall drawer. |
GET /events?since= | Catch up after a dropout. |
GET /bills/<id>/receipt | Everything needed to print a receipt. |
Update, backup and uninstall
In plain words: Replace the module folder with the new version, restart Odoo and run
-u taqnix_restaurant_kds. Your orders, bills, tickets and settings are kept. Update the apps with the new source and rebuild; the API is versioned (/v1), and older apps keep working with newer modules within the same version.In plain words: Back up the database and the filestore as usual. The live event log keeps only 48 hours and is cleaned automatically. To uninstall, use Apps → Restaurant Waiter App → Uninstall; the POS orders and invoices created for paid bills stay in Odoo.
Troubleshooting
| Problem | Cause and fix |
|---|---|
| The app says no restaurant is set up | Tick Waiter app and kitchen display in the restaurant and make sure the user belongs to the same company. |
| Tickets do not appear on the KDS | Check the station of the dish (chapter 10) and that the screen shows the right station or Expo; look at the connection chip. |
| Everything works but nothing live | The websocket is blocked: route /websocket to the gevent port in your proxy (chapter 21) or set proxy_mode correctly; the app polls meanwhile. |
| Open a point-of-sale session at payment | Open a session for the restaurant in Point of Sale. |
| Ingredients are not consumed | The dish needs a bill of materials and Consume ingredients must not be Never; check the stock location. |
| The stock of the dish goes down twice | The dish is a stored product: make it not tracked in stock. |
| Arabic receipt prints as boxes | Set the print mode to Image. |
| Printer does not connect on iPhone | iOS supports only Bluetooth Low Energy printers; use a network printer or an Android device. |
| The app cannot reach a http server on iOS or Android | Enable local network access with the setup script option or use https. |
| A table stays red or blue | An order is still open: open Restaurant → Open tables, check it, or cancel it as a manager. |
Support
In plain words: Updates are free. Installation help and bug fixes are included for 6 months.
Installation help and bug fixes are included for 6 months, with lifetime updates. Contact us through the marketplace page where you bought the module or write to support@taqnix.com with your Odoo version and a screenshot.