Resto · version 1.0
Restaurant point of sale & management
Everything you need to run a restaurant from one install: a touch till that works offline, a kitchen screen, recipe-level stock, a QR menu your customers order from, payroll, and books that balance. This guide covers installing it and running it.
In a hurry? Upload the files, open your domain, and follow the installer. Then read Your first hour — it is the shortest path from an empty install to ringing up a real sale.
Welcome
Resto is a single-restaurant system. One install runs one business, which can have several branches — each with its own stock, prices, staff, tables and reports.
What is in the box
Touch POS
Dine-in, takeaway, delivery and drive-thru. Splits, holds, voids, refunds, cash-up. Keeps taking orders when the internet drops.
Kitchen display
Tickets routed per station, timed against a target, with a bump screen and a recall.
Dining room
A floor plan you draw once, table sessions, transfers, merges and a waiter app for phones.
Inventory
Recipes down to the gram, moving-average costing, batches, wastage, stocktakes and transfers.
Purchasing
Suppliers, purchase orders, partial deliveries, landed cost and what you owe.
Online store
A QR menu search engines can read, guest checkout, order tracking, delivery zones and riders.
Customers
Loyalty points, gift cards, coupons with real rules, and moderated feedback.
Staff
PIN clock-in at the terminal, timesheets, leave, and payroll that reads the clock.
Accounting
Double-entry books that post themselves from sales, deliveries, expenses and payroll.
How to read this guide
Sections follow the order you will actually need them: set the place up, build the menu, open the till, then everything else. Every screen named here exists in the product — where something is planned but not built, it is listed under Not built yet rather than described as if it were.
Server requirements
Resto runs on ordinary shared hosting. There is nothing to compile, no Node on the server, no Redis, no websockets and no Supervisor.
| What | Needs | Notes |
|---|---|---|
| PHP | 8.3 or newer | 8.3 and 8.4 are both tested. |
| Database | MySQL 8.0+ or MariaDB 10.6+ | The installer creates the database for you if the user is allowed to. |
| Extensions | pdo_mysql, mbstring, openssl, tokenizer, xml, ctype, json, bcmath, fileinfo, curl, gd | All standard. The installer checks each one and tells you which is missing. |
| Optional | zip, intl | Nice to have; nothing fails without them. |
| Writable | .env, storage/, bootstrap/cache/, public/uploads/ | Usually 755 on folders, 644 on files. |
| Web root | Point the domain at public/ | See the warning below if your host will not let you. |
Point the domain at public/, not at the folder above it.
If the document root is the project folder, anyone can download your .env
— which contains the database password and the application key. On cPanel this is
Domains → Document Root. If your host truly will not allow it, move the
contents of public/ into the web root and edit the two require
paths at the top of index.php.
What you do not need
No cron job. No queue worker. No Node or npm on the server — the compiled CSS and
JavaScript ship inside the ZIP in public/build/. Nothing in Resto is queued
or scheduled: the kitchen and dispatch screens poll every few seconds rather than
holding a socket open, precisely so it runs on hosting that forbids long-running
processes.
Installing
Resto installs itself in a browser. You do not need shell access, phpMyAdmin or a command line.
-
Upload and unzip
Put the contents of the ZIP on your server and point the domain at the
public/folder. Everything is included — you do not need to runcomposer install. -
Open your domain
Any address on the site redirects to
/setupuntil the install is finished. You will land on the requirements check. -
Requirements
Every required row must be green. If one is not, enable the extension or fix the folder permission on the server and press Re-check. Nothing has been written yet.
-
Restaurant
The name, currency and timezone, plus the environment and URL. The currency decides how every price in the system is displayed, and the timezone decides which business day a 1 a.m. order lands on. Leave Debug mode off on a live server.
-
Database
Host, database name, username and password. Press Test connection before continuing. If the database does not exist yet, Resto creates it — as long as your database user is allowed to. Leave Drop existing tables off unless you are re-installing over a failed attempt.
-
Owner
Your name, email, password and a terminal PIN. The PIN is what the till asks for — clocking in, approving a void, applying a discount. Nobody types an email address on a busy Friday.
Leave Install sample data off for a real restaurant. It fills the system with a demo menu, floor plan, staff and a week of fake orders, which is a lot to delete afterwards.
-
Review and install
Check the summary, then press Install now. This writes
.env, creates the tables and your account. It usually takes under a minute; the log tells you exactly what happened, and exactly where it stopped if something went wrong.
The installer seals itself. Once it finishes, /setup
redirects to the site and cannot be re-run. The seal is a file:
storage/app/installed. Delete it to open the wizard again — and if
/setup is ever reachable when it should not be, that file is the first
thing to check.
If the install fails
The log on screen stops at the step that failed and shows the reason. Nothing after that point was changed. The usual causes:
| Message mentions | What to do |
|---|---|
| Access denied for user | Wrong database username or password. Go back a step and re-test. |
| Unknown database | Your database user may not create databases. Create an empty one in your hosting panel and use that name. |
| Permission denied / failed to open stream | .env or storage/ is not writable. Set folders to 755 and files to 644. |
| Maximum execution time | Rare, and only with sample data on slow hosting. Re-run with Drop existing tables on and sample data off. |
Your first hour
An empty Resto cannot ring up a sale, because nothing has a price yet. This is the shortest route from installed to trading.
-
Check your currency and timezone
Settings → General Settings. The installer set both from what you typed; this is where you correct them, add your logo and set your receipt header.
-
Set your taxes
Settings → Taxes & Charges. Create the rates you charge and put them in a tax group. Decide now whether your prices include tax — the switch is on General Settings → Money & tax. It changes what every number on the menu means, and changing it later does not restate the sales you have already taken.
-
Create a price book
Menu → Price Books. One book marked as the default is enough to start. Prices live in a book, not on the item, which is what lets a takeaway price differ from a dine-in one later.
-
Build the menu
Menu → Categories, then Menu Items. Each item needs at least one size (a variant) and a price in your default book. An item with no price in any book cannot be sold — the till and the online menu both hide it rather than sell it for nothing.
-
Add your staff
Staff. Give everyone a role and a terminal PIN. The role sets a sensible starting set of permissions; tick and untick from there.
-
Draw the floor, if you seat people
Tables & Floor. Add a floor, drop tables onto it and set the seats. Skip this entirely if you are takeaway only.
-
Open the till
POS Terminal. Pick a terminal, open a shift with the cash in the drawer, and take an order. That is the whole loop.
Everything else — stock, suppliers, the online shop, loyalty, payroll, the books — can wait until you are trading. None of it is required to take money.
Command-line install
If you have shell access and would rather not use the wizard:
composer install --no-dev --optimize-autoloader
cp .env.example .env
php artisan key:generate
# edit .env: APP_URL, APP_TIMEZONE and the DB_ settings
php artisan migrate --force
php artisan db:seed --force # branch, owner, payment methods, chart of accounts
npm install && npm run build # only if you changed the CSS or JS
# tell the installer it has nothing left to do
echo OK > storage/app/installed
The seed creates a placeholder owner, owner@resto.test, with the password
password.
Change or delete that account before you go live. It has a published email and a published password. The browser installer deletes it automatically once you create your own owner; a command-line install does not.
Sample data
Optional, and never part of a real install. Run them in this order:
php artisan db:seed --class=DemoRestaurantSeeder # staff with terminal PINs
php artisan db:seed --class=DemoMenuSeeder # a small bistro menu
php artisan db:seed --class=DemoFloorSeeder # a dining room and a terrace
php artisan db:seed --class=DemoInventorySeeder # ingredients, recipes, opening stock
php artisan db:seed --class=DemoChannelSeeder # customers, coupons, delivery zones
php artisan db:seed --class=DemoHrSeeder # timesheets and pay terms
php artisan db:seed --class=DemoSalesSeeder # a fortnight of trading
| Demo account | Role | PIN |
|---|---|---|
manager@resto.test | Manager | 1234 |
cashier@resto.test | Cashier | 4821 |
waiter@resto.test | Waiter | 5150 |
chef@resto.test | Chef | 7788 |
store@resto.test | Storekeeper | 3030 |
All demo accounts use the password password. Delete them before going
live.
Settings
Settings → General Settings. Resto stores its settings in the database,
not in .env, so nothing here needs a file editor. The installer writes the
important ones; the rest have sensible defaults and can be left alone.
Six tabs, each saving only its own keys — changing the receipt footer cannot reset your currency. The key names below are what the tables store, given here because they are also what the API and the seeders use.
| Tab | Covers |
|---|---|
| Restaurant | Name, timezone, logo and browser icon, footer text, languages. |
| Money & tax | Currency, symbol position, cash rounding, tax-inclusive pricing, service charge, tax number. |
| Orders & service | Order numbering, the trading-day start, kitchen target time, stock deduction, PIN approvals. |
| Receipts | Paper width, header and footer, auto-print — with a live preview of the paper. |
| Reservations | Turn time, slot spacing, auto-confirm. |
| API | Rate limit. |
Three things keep their own screens, because each is a set of records rather than a set of switches: tax rates under Settings → Taxes & Charges, the loyalty scheme under Loyalty, and opening hours and delivery under Delivery → Online Store — those differ per branch.
Money
| Key | Default | What it does |
|---|---|---|
currency_code | USD | Three-letter code. Used by the API and by every exported file. |
currency_symbol | $ | What is printed beside the number. |
currency_decimals | 2 | Set to 0 for currencies with no minor unit. |
currency_position | before | before or after the number. |
cash_rounding_increment | 0 | Rounds cash totals to the nearest increment — 0.05 where the smallest coin is five cents. The difference is recorded, not hidden. |
tax_inclusive | 0 | 1 if your menu prices already contain tax. |
service_charge_percent | 0 | Added to dine-in orders. |
service_charge_taxable | 1 | Whether tax applies to the service charge. |
Orders and receipts
| Key | Default | What it does |
|---|---|---|
order_number_prefix | ORD- | Prefix on every order number. |
order_number_daily_reset | 0 | 1 restarts the sequence each day. |
business_day_start_hour | 4 | The hour a trading day begins. A shift that finishes at 2 a.m. belongs to the night it started, not to the next morning. |
receipt_width | 80 | Paper width in millimetres — 58 or 80. |
receipt_header / receipt_footer | — | Free text above and below the receipt. |
auto_print_receipt | 0 | Opens the print dialog as soon as an order is paid. |
tax_number | — | Your VAT / tax registration number, printed on receipts. |
kds_default_sla_minutes | 15 | Target time for a kitchen ticket. Colours the ticket amber, then red. |
inventory_deduct_on_sale | 1 | Takes ingredients out of stock when a sale closes. Turn off until your recipes are built. |
Branding
| Key | What it does |
|---|---|
app_name | Shown in the back office, on receipts and on the online menu. |
logo_path / favicon_path | Paths under public/uploads/. |
show_site_name | Whether the name appears next to the logo. |
dashboard_footer_text | Small print at the bottom of the back office. |
default_language | en. Resto ships English and Arabic, with a full right-to-left stylesheet. |
Branches
Settings → Branches. One Resto install runs one business. A business can have several branches, and almost everything is scoped to one: stock, tables, terminals, shifts, staff, orders and reports. The menu, ingredients and customers are shared.
A single-site restaurant can ignore this screen entirely — the installer creates one branch and names it after the restaurant.
Adding one
A new branch gets a main store automatically, because one with nowhere to hold stock cannot receive a delivery. After that it needs its own floor plan, opening hours and delivery zones — the branch form links to all three.
Two rules
- Exactly one default. Making a branch the default demotes whichever was. Staff with no branch of their own land there, and the online shop uses it when no branch is named.
- A branch is switched off, never deleted. Orders, stock movements and shifts all point at the row. Switching it off hides it from the till and the shop and leaves the history readable. The default branch, and the last one trading, cannot be switched off at all.
A staff member with no branch set can work at any of them. Set a branch on somebody who only ever works at one, and their screens default to it.
A staff member with no branch set can work at any of them. Set a branch on somebody who only ever works at one, and their screens default to it.
Staff, roles & permissions
Staff in the sidebar. Every person is one row, whether they log into the back office, clock in at the till, or both.
Roles
A role is a starting point, not a cage: it fills in a sensible set of permissions which you then tick and untick per person.
| Role | Starts with |
|---|---|
| Owner | Everything, always. The permission list does not apply. |
| Manager | POS, orders, tables, kitchen, menu, inventory, purchasing, customers, reservations, delivery, staff, reports |
| Cashier | POS, orders, tables, customers |
| Waiter | POS, orders, tables, reservations |
| Chef | Kitchen, orders, inventory |
| Storekeeper | Inventory, purchasing |
| Accountant | Accounting, reports, purchasing |
| Rider | Delivery |
Permissions
Fourteen sections: pos, orders, tables,
kitchen, menu, inventory, purchasing,
customers, reservations, delivery,
staff, accounting, reports,
settings.
A link somebody cannot use is dimmed in the sidebar, and the route behind it refuses them as well — pasting an admin URL into a cashier's browser gets a 403, not a page.
Passwords and PINs
Two separate credentials, deliberately. The password signs into the back office. The PIN is what the terminal asks for: clocking in, switching user mid-shift, approving a void or a discount. Four to eight digits.
People who leave
Deleting a member of staff would orphan every order they rang up and every line they approved, so Resto marks them as left instead: the account is deactivated, dated, and hidden from the staff list unless you tick Include leavers. Their history stays intact.
Taxes & charges
Settings → Taxes & Charges. A tax is one rate. A tax group is the set of taxes that apply together — most restaurants need one group with one rate in it, but the structure handles a state tax plus a city tax without inventing a combined percentage.
Every menu item points at a tax group. Items with none use the default group.
Inclusive or exclusive
The single most important decision on this screen.
- Exclusive (
tax_inclusive = 0): the menu says 10.00, the customer pays 10.00 plus tax. - Inclusive (
tax_inclusive = 1): the menu says 10.00 and that is what the customer pays; Resto works backwards to find the tax inside it.
Set this before you take your first order. Changing it later does not restate sales already recorded — every order stores the totals it was rung up with, on purpose, so a settings change next month cannot rewrite what last month's revenue was.
Service charge
A percentage added to dine-in orders, set with service_charge_percent.
Whether it is itself taxable is a separate setting, because the answer differs by
country. It is never added to takeaway or delivery.
Categories
Menu → Categories. Categories group items on the till grid and on the online menu, and they can nest one level. Each one can carry a colour and a default kitchen station, which items inherit unless they override it.
Drag to reorder — the order here is the order on the till, so put what sells most first.
Items & variants
Menu → Menu Items. An item is a thing on the menu. A variant is a size or version of it — Regular and Large, or a single default variant for something that only comes one way.
Prices are not on the item. A price is the intersection of a variant and a price book. That is what lets delivery cost more than dine-in without duplicating the menu.
What an item carries
| Field | Why it matters |
|---|---|
| Category | Where it appears on the grid and the online menu. |
| Kitchen station | Which screen the ticket prints to. Falls back to the category's station. |
| Tax group | Overrides the default group for this item. |
| Channels | Where it may be sold: till, QR, online, delivery. An item absent from a channel is not merely hidden there — the order service refuses it. |
| Schedule | Available from / until, and on which days. Breakfast stops at 11. |
| Prep time | Feeds the kitchen timer and the online ETA. |
| Dietary tags & allergens | Printed on the online menu; several markets require it. |
| Image | Used on the till grid and the QR menu. |
Combos
An item of type combo carries choice groups instead of a plain price — a meal deal where the customer picks a main, a side and a drink. Combo groups are edited inside the combo item itself, on the item form.
Import and export
The item list exports to CSV and imports back. The export is the template: change it, re-upload it. Useful for a first bulk load and for a price review.
Modifiers
Menu → Modifiers. A modifier group is a question the till asks — "How would you like it cooked?", "Any extras?" — and the modifiers are the answers.
Attach a group to as many items as you like. Each item can override the group's rules, so Extras can allow three free choices on a burger and none on a salad.
| Rule | Effect |
|---|---|
| Required | The order cannot be sent until something is picked. |
| Min / max select | How many answers are allowed. |
| Free count | How many are included before charging. Spent in units, so "extra bacon ×2" uses two of three free picks. |
| Max quantity | How many times one modifier may be repeated. |
| Price & cost | What it adds to the bill, and what it takes out of stock. |
Combos
Menu → Combos. A combo is a meal deal: one price, and the customer picks a main, a side and a drink.
Making one
Create a menu item of type Combo
It gets a price like any other item — that is what the customer pays.
Add its choice groups
On the item's own page. "Choose a main", "Choose a drink". Each group says how many the customer picks.
Put options in each group
Any variant on the menu can be an option.
How it prices
The combo carries the price and the choices are free — unless an option sets an upgrade charge, the "large fries, +1.00" case. A negative charge is allowed, for a cheaper swap.
A combo with no choice groups cannot be ordered — the till has nothing to ask for. The Combos screen flags those at the top so they do not sit unsellable on the menu.
The Combos screen exists because the item list can tell you a combo is there but not what is in it, which is the only thing about a combo that matters. Groups are still edited inside the item, because a group has no meaning away from the combo it belongs to.
Price books
Menu → Price Books. A price book is a named set of prices. One book is the default and is used whenever nothing more specific applies.
A book can be limited by branch, channel, order type, day and time. Resto picks the first book whose conditions match the sale, and falls back to the default for any item that book does not price.
What that buys you
- A delivery book priced 15% above dine-in, to cover the aggregator's cut.
- A happy-hour book live from 4 to 6 on weekdays.
- A second branch in a more expensive part of town.
- A staff book at cost.
An item that no book prices cannot be sold. The till hides it and the online menu leaves it out, rather than offering it for nothing.
Availability (86)
Menu → Availability (86). "86" is kitchen shorthand for we have run out. This screen takes an item off the menu without deleting anything.
- Per branch — out at one site, still on at another.
- A whole item, or just one size.
- Until a time you choose, or until somebody puts it back.
86-ed items grey out on the till and drop off the online menu immediately.
The POS terminal
POS Terminal in the sidebar, or go straight to /pos. Built for a
finger on a tablet: large targets, no hover, no double-tap.
Terminals
A terminal is a named till — Front counter, Bar. Each has its own shift, its own drawer and its own receipt numbering. Pick one the first time you open the POS on a device; it is remembered.
Taking an order
Choose the order type
Dine-in, takeaway, delivery, drive-thru or room service. This decides the price book, whether a service charge applies and whether a table is needed.
Tap items
Tap a category, tap an item. If it has sizes or required choices, the till asks. The running total is on the right.
Adjust the line
Tap a line to change the quantity, add a note for the kitchen, apply a discount or void it.
Send to the kitchen
Sends only the lines that have not been sent yet — adding a round sends the round, not the whole table again.
Take payment
One tender or several. See Payments.
Holds
Park an order with a label and recall it later. A held order has not been sent to the kitchen and owes nothing.
Discounts and voids
Both ask for a reason and both are recorded against the person who approved them, in the activity log. An order-level discount is spread across the lines in proportion to what each contributes, before tax — taking it off the total afterwards would quietly reduce the tax owed on a sale that already happened.
Shifts & cash-up
A shift is one session on one terminal: opened with the cash in the drawer, closed by counting it.
Open
Enter the opening float. Nothing can be rung up on a terminal with no open shift.
During service
Cash in and cash out — a supplier paid from the drawer, a float top-up — are recorded as they happen, each with a reason.
Close
Count the drawer and enter the figure. Resto shows what it expected, what you counted and the difference.
Expected cash is derived from the movements ledger, never from a running total anybody can edit. The Z-report is stored on the shift, so a cash-up can be re-read months later.
Staff → Shifts & Cash-up lists every shift with its variance. A small shortage that repeats is a different problem from one large one, and the list is sorted so you can see which you have.
Payments & refunds
Payment methods are set up in the foundation seed: cash, card, wallet, gift card and account. Each carries whether it counts in the drawer, whether it can give change and whether it needs a reference.
Splitting a bill
A bill split four ways is four payment rows, not one payment with a note — that is the only shape a payment-method report and a cash-up can be built from.
Change
Calculated on what was tendered, and only for methods that can give change. Handing cash back against a card tap is not a rounding error, it is a hole in the drawer.
Refunds
A closed order is never edited. A refund is a new record that quotes the original, picks the lines being returned, records who approved it and why, and puts the stock back if you say so. The original order still says what it said.
Printing
Settings → Printers & Receipts.
There is nothing to install. Resto prints through the browser, so any printer the device can already see works — thermal, laser or a PDF. Set it as the default on the device, turn off the browser's own headers and margins, and the documents come out right.
The customer receipt
Paper width (58 or 80 mm), a header under the restaurant name, a footer at the bottom, and whether it prints itself the moment an order is paid. The screen shows a live preview of the paper, because "58 or 80" means nothing until you see how much of your address fits on it.
The kitchen ticket
For kitchens with a printer rather than a screen — a docket in a rail is still the fastest way to run a pass. Bigger type than a receipt, quantities and item names in capitals, modifiers indented, notes boxed and allergies boxed heavily: it is read at arm's length, in a hurry, through steam.
Tickets route to a station the same way they do on the Kitchen Display, so a kitchen can use screens, printers, or a screen on the grill and a printer at the pass.
Testing it
Both documents have a test print button that uses a real order or a real ticket — not a mock-up, so what you see is what a customer gets. Save the settings first; the test reads them as stored.
Working offline
The till keeps working when the internet drops. This is not a bonus feature — a restaurant that stops taking money because a router rebooted has a serious problem.
How it works
- A service worker keeps the terminal loadable with no connection.
- The menu is cached on the device, so items and prices are there.
- Orders taken offline queue in the browser and send themselves when the connection returns.
- Every order carries an identifier generated on the device, so a replay after a dropped connection is a quiet success rather than a duplicate sale.
What it will not do
| Offline | Behaviour |
|---|---|
| Cash sales | Work normally. |
| Card terminals | Depend on your card provider, not on Resto. |
| Kitchen tickets | Queue, and arrive when the connection returns. |
| Stock deduction | Happens on the server when the order syncs. |
| A brand-new item | Not on the device until it next syncs the menu. |
Do not close the browser tab while orders are still queued. The badge on the POS shows how many are waiting to send.
Dining room
Tables & Floor to draw the room, Service → Floor to work it.
The plan
Add a floor — Dining room, Terrace — then drop tables onto a grid, set the seats and the shape, and drag them where they really are. The grid is in units rather than pixels, so the same layout renders on a phone and on a desktop.
During service
Each table shows its state at a glance: free, seated, ordered, food served, bill asked for, needs clearing. From a table you can seat a party, open its order, transfer it to another table, merge two tables for a large group, or clear it down.
A table that still owes money cannot be cleared. Settle or transfer the bill first.
QR codes
Delivery → Online Store → QR codes prints a sheet of table cards. Each carries a stable token, so reprinting the sheet does not invalidate the codes already stuck to the tables. Scanning one opens the menu and remembers which table it came from.
Kitchen display
Kitchen Display, or /kitchen. Designed for a screen on a wall,
seen from two metres away with steam in between.
Stations
Settings → Kitchen Stations. Grill, fryer, cold, bar. Items route to a station; the station's screen shows only its own tickets. A screen can also watch several stations at once for a small kitchen.
The board
- Tickets arrive as they are sent from the till and are ordered oldest first.
- Each carries a timer, amber at three quarters of the target and red past it.
- Tap a line to mark it ready; tap the ticket to bump it off the board.
- A bumped ticket can be recalled — the wrong bump is the most common mistake on a kitchen screen, and it should not cost anybody a plate.
The board refreshes itself every few seconds. It does not need a websocket, and it does not redraw unless something has actually changed.
Waiter app
Waiter App, or /waiter. A phone-sized view for the floor: your
tables, what is ready to run, and what is still coming.
It is the same login and the same permissions as everything else — no separate app to install and no separate account to manage.
Ingredients & units
Inventory → Ingredients. An ingredient is something you buy and hold: flour, chicken thigh, a bottle of tonic.
Units
Every ingredient has a base unit — the unit it is counted in. Purchase units convert to it, so you can buy a 25 kg sack and use 180 g without doing the arithmetic yourself.
Costing
Resto uses moving average cost. Each delivery blends its price into the existing stock, so the cost of a portion reflects what you actually paid rather than the last invoice. Ingredients marked track batches also carry batch codes and expiry dates, and are drawn oldest first.
Preps
An ingredient marked is prep is something you make: a sauce, a dough, a stock. It has a recipe of its own and can be held in stock like anything else.
Recipes
Inventory → Recipes. A recipe says what one portion of a menu item consumes. Build them and two things start working: food cost on every report, and stock that goes down when you sell.
Sub-recipes do the right thing. If a burger uses your own barbecue sauce and you hold that sauce in stock, selling a burger takes sauce out — not flour, sugar and tomato. If you do not hold it, Resto explodes the recipe down to raw ingredients instead. Costing always explodes to raw, so the number is honest either way.
Recipes attach to a variant, so a Large can use more than a Regular. Modifiers carry their own cost, so extra cheese takes cheese out of the walk-in.
Stock, wastage & stocktakes
Stores
A store is a place stock sits: the kitchen, the bar, the dry store. Every movement names one.
The ledger
Stock is an append-only ledger of movements, with the on-hand figure derived from it. Nothing writes a level directly. That means every number can be explained: tap a level to see exactly what made it.
Wastage
Inventory → Wastage. Record what was thrown away and why. It comes off stock and lands in the books as a cost, separate from a stocktake difference — one is a kitchen problem and the other is a counting problem, and you need to know which you have.
Stocktakes
Inventory → Stocktake. Open a count, enter what is physically there, and close it. Resto shows the variance per line — expected against counted, in units and in money — and posts the difference when you commit.
Transfers
Inventory → Transfers. Move stock between stores or between branches. One movement out, one movement in, both recorded.
Purchasing
Suppliers
Purchasing → Suppliers. Contact details, payment terms, lead time and a running balance of what you owe. Each supplier can carry its own price per ingredient.
Purchase orders
Purchasing → Purchase Orders. Draft, approve, send. The reorder suggestion reads what is below its reorder level and which supplier is cheapest per base unit, so a weekly order is mostly a matter of checking a list.
Goods receipts
Purchasing → Goods Receipts. Booking in a delivery is the only document that touches stock. It handles what actually happens at a back door:
- Partial deliveries. Order 20 kg, receive 12 today and 8 on Thursday. The order closes itself when nothing is outstanding.
- Landed cost. Freight and duty are spread across the lines by value, so an expensive line carries more of the delivery charge than a cheap one.
- Returns. Stock goes back out and the debt shrinks.
What you owe goes up with the delivery, not with the order — an order is a promise, a delivery is a debt.
Customers
Customers. One row per person, whether they were added at the counter or signed up on the website. Phone number is the practical identity: people give a number at a till and an email only when a form insists.
Each customer carries their order history, what they usually order, saved addresses, points, gift cards, and an allergy note that appears on every kitchen ticket for them.
Segments
Worked out from the order count and the last order date, not stored: New, Active, Slipping away, Lost. The filter on the list uses the same definition, so the regular who has not been in for four months is one click away.
Loyalty & gift cards
Customers → Loyalty.
Points ship switched off. A loyalty scheme is a real liability — every point is something a customer can walk in and spend — so Resto does not start accruing one until you decide to. Turn it on at the bottom of the Loyalty screen.
How it earns
| Setting | Meaning |
|---|---|
| Points per unit | How many points one unit of currency earns. |
| Point value | What one point is worth when spent. |
| Minimum to redeem | Stops a two-point redemption at the till. |
| Expiry | In months. Zero means never — check your local rules. |
Points are earned on the net of an order, not the gross. Paying points on tax is giving the taxman's money away twice. Earning is recorded once per order, so a replayed payment cannot pay out again.
Gift cards
Customers → Gift Cards. Issue a card for an amount; it gets a code and a balance. Spending one is a payment method at the till. A card cannot be overspent — it pays what it has and the rest is settled another way.
Both points and gift cards are ledgers with a derived balance. Nobody types a balance in, and the Loyalty screen shows the total liability so you always know what you owe.
Coupons
Customers → Coupons. Four kinds: a percentage off, a fixed amount off, a free item, or free delivery.
The rules are deliberately expressive, because "10% off" is the easy half. Every restaurant eventually wants free delivery over 25, Tuesdays only, first order, one per customer:
| Rule | What it does |
|---|---|
| Cap the discount | On a percentage. Without it, a percentage off a party of twenty costs whatever they ordered. |
| Minimum spend | Below it the code is refused — with a message that says how much more is needed, not just "invalid". |
| Channels & order types | Online only, delivery only, and so on. |
| Days & times | Tuesdays, or between 3 and 5. |
| Date window | From and until. |
| Total uses | How many times in all. |
| Uses per customer | The one that actually stops abuse. |
| First order only | For a welcome code. |
| Branch / customer group | Limit it to one site or one group. |
A code is only spent when an order exists, so an abandoned basket never burns a single-use code. A code that has been used cannot be deleted — it is switched off instead, because deleting it would orphan the discount on somebody's receipt.
Online store & QR menu
Delivery → Online Store configures it; the shop itself lives at
/shop.
The menu page
Server-rendered, not a JavaScript app. A QR menu a search engine cannot read is a marketing asset thrown away, and somebody standing outside your door on a weak signal should get text before they get a framework.
Ordering
Guest checkout by default. Forcing a stranger to register before they can buy a pizza is the most reliable way to lose the sale. An account is offered — it saves an address and counts points — never required.
Online orders are ordinary orders. They appear on the same list, print the same kitchen tickets and land in the same reports as anything rung up at the till. Prices come from the same price engine, so an integration cannot sell at a price you never set.
Order tracking
Every order gets a tracking page at /shop/order/{id}. The unguessable id
is the whole authorisation — a guest has no account to log into. The page polls for
progress and asks for a rating once the order is finished.
The controls that matter mid-service
| Control | When you reach for it |
|---|---|
| Busy mode | 8 p.m. on a Friday, kitchen drowning. Adds minutes to every quoted time without closing the shop. |
| Pause online orders | Stops new orders entirely. Existing ones carry on. |
| Opening hours | Per day, per branch. A closing time before the opening time means service runs past midnight. |
| Prep time | The normal quote when things are calm. |
Delivery
Zones
Delivery → Delivery Zones. A zone is either a list of postcodes or a radius from the branch. Each carries a fee, a minimum order, an optional free-over threshold and an ETA.
Zones are checked in order, so a specific postcode zone can sit in front of a catch-all
radius. Postcodes match on the outward part and ignore case and spacing — an entry of
SW1 covers everything in SW1.
Without at least one zone the shop refuses every delivery order. A radius zone also needs the branch latitude and longitude, set on the Online Store screen.
The dispatch board
Delivery → Dispatch Board. What is waiting, who is out, and how late it is. Assign a run to a rider or let Resto pick whoever has fewest in progress, mark it picked up, then delivered or failed.
Riders and cash
Cash collected on a doorstep moves onto the rider and stays there until they hand it in. The board shows what each is holding, and settling it is one button. Without that, the money is missing from the drawer and nobody can say where it went.
The fee is snapshotted onto the order when it is placed, so changing your delivery charge on Friday does not retroactively change what Thursday's customer was quoted.
Reservations
Front of House → Reservations. One day at a time, because that is how a host reads the book.
Availability is worked out from capacity and turn time rather than from a fixed grid of slots: six four-tops seat rather more than six parties across an evening, and a booking system that pretends otherwise turns away money.
- Bookings taken online wait for a human unless you switch auto-confirm on. Bookings taken by staff are confirmed on the spot.
- A host on the phone can overbook deliberately — there is a book it even if we look full switch, for when you know something the grid does not.
- Seating a booking opens a table session, so the party lands on the floor plan.
- No-shows are recorded, which is worth knowing about a number that does it repeatedly.
Waitlist
On the same screen. Add a walk-in with a party size, and Resto quotes a wait based on the queue in front of them rather than a flat guess. Call them when a table frees, then seat them straight onto it.
Feedback
Customers → Feedback. Customers are asked to rate an order on the tracking page once it is finished.
Nothing is published until you publish it — an unmoderated review widget on a restaurant website is a liability, not a feature. The point of this screen is not the five-star ones; it is finding Saturday's two-star before the customer gives up and puts it somewhere public. The Needs an answer filter shows exactly those.
Attendance & leave
The clock
Staff clock in at the terminal: /pos/clock, a keypad and a PIN. It is
built for a doorway, with wet hands and a coat still on.
Breaks are unpaid and are subtracted from the shift. A break left running is closed with the shift rather than quietly paid for. Nobody can be clocked in twice — two taps a second apart would otherwise pay one shift twice.
A shift that starts at 10 p.m. and ends at 2 a.m. belongs to the night it
started, controlled by business_day_start_hour.
Timesheets
Staff → Attendance. Who is in the building right now, hours per person for the period, and every entry with the ability to correct one. Corrections record who made them and when — an edited timesheet that does not say who edited it is worth nothing in a dispute.
Leave
Staff → Leave. Holiday, sick, unpaid or other. Days are counted as working days, so Monday to Friday is five and not seven. Approved paid leave appears on the payslip at the person's normal rate, and the panel on the right shows who is off in the next fortnight — which is what the rota actually needs.
Payroll
Staff → Payroll. A pay run reads the timesheets for a period and draws up a payslip for everyone.
Resto does not calculate payroll tax. It changes by country and by year, and getting it subtly wrong is worse than not offering it. Deductions are entered as a figure your accountant gives you.
What it works out
- Hours and overtime. Overtime is counted week by week, not across the whole period — fifty hours one week and thirty the next is ten hours of overtime, not zero.
- Salaries, pro-rata across the period.
- Tips, split by hours worked among those who share in the pool. The last share absorbs the rounding, so the shares add back to the pool exactly.
- Commission, on what each person personally sold.
- Paid leave, at the normal rate.
Draft, approve, pay
A draft rebuilds from the clock as often as you like — somebody always forgets to clock out and the timesheet gets fixed afterwards. Approving freezes the payslips and posts the cost to the books. After that a correction is a fresh run, not an edit: the point of a payroll record is that it says what was actually paid.
Rate and pay type are copied onto each payslip, so a rise next month does not rewrite last month's.
Accounting
Double-entry books that keep themselves. Everything the restaurant already records — a sale, a delivery, an expense, a pay run — posts itself as a balanced journal. Nothing is typed in twice.
Chart of accounts
Finance → Chart of Accounts. The installer creates a short default chart. It is deliberately small: a restaurant needs to know what it sold, what the food cost, what it spent and what it owes. Anything longer is a chart nobody maintains and every posting lands in "Miscellaneous".
Rename or renumber anything freely — the automatic postings look accounts up by a hidden key, not by name or code. Accounts marked system cannot be deleted for that reason.
What posts automatically
| When | The books record |
|---|---|
| A sale closes | Cash or card in, revenue out, tax owed, tips owed, discounts given — plus the food leaving stock at cost. |
| A delivery is booked in | Stock up, supplier owed. |
| A supplier is paid | Supplier owed down, bank or cash down. |
| An expense is recorded | The category's account up, bank or cash down. |
| Stock is wasted or counted | Wastage or variance, against stock. |
| A pay run is approved | Wages, tips discharged, net owed to staff. |
Tax and tips are never revenue. Tax collected is money held for the authority; tips belong to the staff. Both are debts, and booking either as income overstates the profit built on it.
Four rules
- Debits equal credits. An unbalanced journal cannot be saved.
- A journal is never edited. A mistake is corrected by a reversing journal that quotes it, so the books show both the error and the correction.
- One posting per source. A replayed sale is a no-op, not double books.
- Nothing lands in a closed period.
Statements
Trial Balance, Profit & Loss and Balance Sheet all read the same journal lines, so they agree with each other by construction. Each says plainly whether it balances rather than leaving you to add it up.
Period close
Finance → Period Close. Closing a month stops anything being posted or backdated into it, so the figures you gave your accountant in March cannot quietly change in June. A correction after a close is a journal dated today. Reopening a month is possible and is written to the activity log.
Expenses
Finance → Expenses. Rent, power, wages, card fees. Food is not entered here — a goods receipt already records what was spent on ingredients, and entering it twice is the fastest way to produce a profit figure that disagrees with the bank.
Reports
| Report | Answers |
|---|---|
| Sales | Revenue by day, by hour, by order type, by channel, by payment method and by staff member. Average check, covers, and the exceptions worth a second look — voids, discounts, refunds. |
| Food cost | Gross margin per item, and the menu-engineering quadrants: what sells and makes money, what sells and does not, and what does neither. |
| Inventory | Stock valuation, what is below its reorder level, wastage, and the variance a stocktake found. |
| Financial | Profit and loss from the operational data, expenses by group, and a tax summary for a return. |
Every report takes a period and a branch, compares against the previous period, exports to CSV, and prints. Printing uses the browser, which means a PDF on every platform without Resto shipping a PDF engine.
Charts are drawn in CSS rather than canvas: they print, they follow the theme, they need no JavaScript, and they survive being saved as a PDF.
Activity log
Staff → Activity Log. Every void, discount, price override, refund, timesheet correction, reversed journal and reopened period, with who did it, when, from which address.
This is the trail restaurant owners buy POS software for. Filter by person, by action type and by period.
REST API
For a delivery aggregator, a self-order kiosk, a dashboard of your own.
Tokens
Settings → API Tokens. Create a token, choose which member of staff it acts as, and optionally limit it to particular sections. The token is shown once — only a hash is stored, and a token list that can show you the secret is a token list that leaks every secret.
A token can never do more than its owner. Take a permission away from somebody and their integrations lose it too.
Calling it
curl https://yourdomain.com/api/v1/ping \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"
Every response has the same shape — data under data, pagination under
meta — so a client never has to guess where the payload is.
Endpoints
| Method | Path | Needs | What it does |
|---|---|---|---|
| GET | /api/v1/ping | any token | Confirms the token works and reports what it may reach. |
| GET | /api/v1/menu | menu | The menu priced for a channel and order type, with modifiers and availability. |
| GET | /api/v1/orders | orders | Orders, filterable by branch, status, channel and date. |
| POST | /api/v1/orders | orders | Creates an order. |
| GET | /api/v1/orders/{uuid} | orders | One order in full, with lines, payments and delivery. |
| PATCH | /api/v1/orders/{uuid}/status | orders | Moves an order along. |
| GET | /api/v1/stock | inventory | Levels and values, optionally only what is low. |
| GET | /api/v1/ingredients | inventory | The ingredient list. |
| GET | /api/v1/customers | customers | Customers, searchable by name or phone. |
| GET | /api/v1/reservations | reservations | Bookings for a date. |
| GET | /api/v1/summary | reports | The day's numbers for a dashboard. |
Creating an order
POST /api/v1/orders
{
"uuid": "8f14e45f-ceea-467a-9e4c-3b1d2a5f7b90",
"type": "delivery",
"customer": { "name": "Sam", "phone": "07700 900001" },
"lines": [
{ "variant_id": 12, "qty": 2, "modifiers": [{ "modifier_id": 5 }] }
]
}
Send a uuid and retries are free. Replaying the same one
returns the existing order instead of creating a second. Prices are always taken from
your database, never from the payload — a request claiming a steak costs a penny gets a
steak at the menu price.
Rate limits and errors
120 requests a minute per token by default (api_rate_limit), counted per
token rather than per address so several integrations behind one office connection do not
throttle each other.
| Status | Means |
|---|---|
401 | Missing, unknown, disabled or expired token. |
403 | The token may not reach that section. |
422 | The payload was rejected; errors says which field. |
429 | Rate limited. |
503 | This copy of Resto has not been installed yet. |
Payment gateways
Settings → Payment Gateways. Two halves: what the till accepts, and whether the website can take a card.
At the till
Cash, card, store credit, gift card and on-account are set up for you. You can add your own — a meal voucher, a local wallet — and the flags are behaviour, not decoration:
| Flag | What it changes |
|---|---|
| Counts in the cash drawer | Whether it reaches the cash-up. Tick it only for money that physically ends up in the till, or the drawer will never balance. |
| Can give change | Handing cash back against a card tap is a hole in the drawer, not a feature. |
| Pops the drawer | Opens the till on payment. |
| Asks for a reference | An auth code, a voucher number, a card serial. |
Methods are switched off, never deleted — every payment ever taken names one, and removing it would leave old orders unable to say how they were settled. Cash cannot be switched off at all.
Card payments online
Through Stripe Checkout. The customer is handed to Stripe's own payment page and comes back afterwards, so the card number never touches your server — which keeps your PCI obligation to the shortest questionnaire there is. Installing a till should not hand you a compliance project.
-
Create a Stripe account
Copy the two API keys from its dashboard. Start with the test keys — they begin
sk_testandpk_test. -
Paste them in and save
Then press Test the connection. It asks Stripe whether the keys work, so you find out now rather than when a customer does.
-
Add the webhook
The screen shows the address. In Stripe, add it as an endpoint and subscribe it to
checkout.session.completed. Stripe gives you a signing secret; paste that in too. -
Switch it on, and place a test order
Stripe's test card is
4242 4242 4242 4242with any future expiry. Check the order marks itself paid. -
Swap in the live keys
Only once a test order has gone all the way through.
Without the signing secret, no payment will ever be recorded. That is deliberate. The browser is never allowed to say a payment succeeded — anyone can open the page it comes back to. Only a webhook that Stripe signed marks an order paid, and an unsigned, forged or stale one is refused before a single row is read.
What the customer sees
With it switched on, online checkout offers Pay now by card alongside paying on collection or to the rider. The order is created before the payment page opens, so a customer who abandons it leaves you an order to chase rather than nothing at all — and the tracking page carries a Pay by card button so they can come back and finish.
The confirmation comes from Stripe, not from the customer's browser, so an order can read as unpaid for a second or two after they return. It settles itself.
Refunds
Refund a card payment in the Stripe dashboard, then record the refund in Resto as usual so the books and the stock agree. Resto does not push refunds to Stripe — a refund issued by accident from a POS screen is very hard to take back.
Languages
Settings → Languages. Resto ships English, and can be translated into any of the languages listed there — including Arabic, which loads a full right-to-left stylesheet automatically.
There is no template to fill in. Every string in Resto is its own
key — the English sentence itself — so the untranslated product reads as English by
construction, and a string you have not got to yet falls back to English rather than
going blank or showing you messages.save.
Translating
Open a language and you get every string the product uses, in pages of a hundred, with the English on the left and a box on the right. Filter to not translated yet to work through the gap, or search for a phrase you saw on screen.
Each page saves on its own and merges into what is already there, so translating page four does not wipe pages one to three.
Translating offline
Download gives you a JSON file with every string, translated or not — which is also the template to hand to a translator. Import takes it back. Anything in the file that this version does not use is ignored rather than stored, so an edited file cannot fill the table with junk.
Placeholders
Some strings contain a placeholder like :count or :name.
Keep them exactly as they are, in whatever position the sentence needs — the value is
substituted at the placeholder, so moving it is fine and renaming it is not.
Switching one on
A language has to be turned on before it appears in the language menu. The default language is always available whatever its switch says — otherwise the menu would offer a language the site will not serve. Change the default on General Settings.
Backups
Two things need backing up:
- The database. Everything is in it. Use whatever your host provides
— cPanel, a scheduled
mysqldump, your provider's snapshots. public/uploads/. Logos and menu images live here, not on a storage disk.
mysqldump -u USER -p DATABASE > resto-$(date +%F).sql
tar -czf resto-uploads-$(date +%F).tar.gz public/uploads
Keep a copy of .env somewhere safe too. It holds APP_KEY,
and restoring a database without the key it was encrypted alongside will sign everybody
out and invalidate saved sessions.
Updating
Back up first
Database and
public/uploads/. Every time.Keep your own files
Do not overwrite
.env,public/uploads/orstorage/.Copy the new release over the old one
Everything else can be replaced.
Run the migrations
php artisan migrate --force, thenphp artisan config:clearandphp artisan view:clear. If you have no shell, ask your host — this is the one step that needs one.
The install marker stays where it is, so the wizard does not reappear.
Troubleshooting
| Symptom | Cause and cure |
|---|---|
Every page redirects to /setup | storage/app/installed is missing. Finish the wizard, or create the file if the install is genuinely complete. |
/setup loads on a live site | Same file, missing. Anyone reaching it could reinstall over you — create it immediately. |
| Blank white page | Almost always a permission problem. Make storage/ and bootstrap/cache/ writable, then check storage/logs/. |
| 500 after an update | Stale cache. Run php artisan config:clear and view:clear. |
| No styling anywhere | public/build/ is missing or the document root is wrong. Confirm the domain points at public/. |
| An item will not appear on the till | It has no price in any book, it is off for that channel, it is outside its schedule, or it is 86-ed. |
| The online shop says it is closed | Opening hours for today, accepts online orders switched off, or the branch is inactive. All three are on the Online Store screen. |
| The shop refuses every delivery | No delivery zone covers the address — or no zones exist at all. |
| Stock is not moving | The item has no recipe, or inventory_deduct_on_sale is off. |
| The cash-up is short | Check Staff → Shifts for cash movements out of the drawer, and the dispatch board for cash still on a rider. |
| An order has no journal | The chart of accounts was incomplete when it closed. Install the defaults on the Chart of Accounts screen; a posting failure is recorded in the activity log and never blocks a sale. |
| Kitchen screen not updating | It polls; check the device actually has a connection. Nothing here needs websockets. |
Where the logs are
storage/logs/laravel.log. Turn APP_DEBUG on in
.env only long enough to read an error, then turn it back off — a debug page
on a live site leaks your configuration.
Deliberately not here
Every link in the sidebar opens a real screen. A few things a restaurant system could plausibly do are absent on purpose, and this guide would rather name them than let you find out:
- No printer drivers. Printing goes through the browser, so any printer the device can already see works — thermal, laser or a PDF. An ESC/POS driver would mean a vendor library on a shared host and a support queue about USB.
- No card-machine integration. The Card tender at the till records that a card was used; the terminal is your bank's. Integrating one ties a restaurant to a single acquirer, and the ones worth integrating with differ in every country. Online card payments are a different matter — see Payment gateways.
- No automatic updater. One that half-finishes over a live restaurant is worse than four steps you can see.
- No restore button. Restoring overwrites everything, and a mis-click there costs a business its trading history. The command is on the screen instead.
- No payroll tax. It changes by country and by year, and getting it subtly wrong is worse than not offering it. See Payroll.
- No cron and no queue worker. Nothing is scheduled and nothing is queued, so Resto runs on hosting that forbids long-running processes.
Changelog
1.0
First release. POS with offline support, kitchen display and printed tickets, dining room and waiter app, recipe-level inventory, purchasing, QR menu and online ordering with card payments, delivery, reservations, customers with loyalty and coupons, staff with attendance and payroll, double-entry accounting, reports and a REST API — plus a browser installer, a translation manager and database backups.
Credits & support
Resto is built on Laravel 13 with Blade, Tabler UI and Alpine.js. QR codes are drawn by
bacon/bacon-qr-code. Everything else is written for this product.
If something here does not match what you see on screen, the product is right and this document is wrong — please report it, and it will be corrected.
Resto documentation · version 1.0
Thank you for buying it.