- HTML 44.5%
- PHP 30%
- JavaScript 17%
- CSS 7.8%
- PowerShell 0.7%
- orders.html : onglets Commandes / Statistiques (Bootstrap tabs) - Statistiques chargées en lazy à la 1re ouverture, hash #stats supporté - jQuery ajouté dans <head> pour le JS stats existant - Modale confirmation suppression commande (remplace confirm() natif) - stats.html supprimé - Lien /admin/stats.html retiré du nav dans les 10 autres pages admin |
||
|---|---|---|
| docs | ||
| public | ||
| scripts | ||
| sql | ||
| .htaccess | ||
| CLAUDE.md | ||
| menu.pdf | ||
| menu_extracted.txt | ||
| pos (1).sql | ||
| pos.sql | ||
| README.md | ||
Pizza POS MVP (PHP + MySQL)
1) Install
-
Create a MySQL database (utf8mb4).
-
Run:
sql/schema.sqlsql/seed.sql
-
Edit DB credentials in:
public/api/config.php
2) Run locally
From the project root:
php -S 127.0.0.1:8000 -t public
Open:
- POS: http://127.0.0.1:8000/
- Admin prices: http://127.0.0.1:8000/admin/prices.html
2bis) Deploy on OVH under /POS
Target URL: https://domaine.com/POS
- Upload this whole folder to OVH in
www/POS. - Keep the folder structure unchanged (
public/,sql/,docs/, etc.). - Ensure Apache rewrite is enabled on OVH (
mod_rewrite). - Keep the root
.htaccessfrom this repo (it routes web requests topublic/). - Open:
- POS:
https://domaine.com/POS/ - Admin:
https://domaine.com/POS/admin/prices.html
- POS:
Notes:
- API works via
config.jsandapiUrl(...), so/POS/api/...is handled correctly. sql/,docs/are not exposed through normal web routes with this setup.
3) What this MVP does
- Order types: livraison / emporter / sur_place
- Prices differ by context (pickup vs delivery) via product_prices
- Pricing modes:
- normal
- staff_5 (1 main free, others 5; garlic bread 1st free, 2nd+ = 5; drinks 0.50; desserts 2; extras free)
- family_6, family_7
- friends_5, friends_6, friends_7
- gift_0
- family/friends can toggle "drinks free"
- Delivery minimum per commune for TEL + LIVRAISON (override with reason)
- Customer phone lookup, multiple addresses with "save for next time"
- Tickets generated (text):
- Accueil (complete)
- Pizza (pizza station + garlic bread, with "+ PÂTES : X" and commune)
- Cuisine (cuisine station, with "+ PIZZAS : Y" and SOLO/+PIZZA tag)
4) Printing
Architecture — ESC/POS direct LAN
Les tickets sont encodés en ESC/POS binaire et envoyés directement à l'imprimante réseau via TCP (fsockopen() sur port 9100). Aucun bridge Node.js, aucun driver Windows, aucune queue d'impression OS.
L'imprimante est une Star IFBD-HI01X (Star Line Mode). Les commandes ESC/POS utilisées sont celles de Star (pas Epson GS V / ESC !).
Flux complet :
1. POS (browser) → POST api/order_create.php
→ Insère commande en DB (transaction)
→ Génère les tickets ESC/POS (build_all_tickets_escpos)
→ Enqueue 1 job/ticket dans print_jobs (base64)
→ Répond {ok, order_id, total, tickets}
2. POS → POST api/bridge_print.php {order_id}
→ Drain les jobs pending de cet order (envoi TCP direct à l'imprimante)
→ Si imprimante KO : jobs restent pending, le worker reprend
3. Cron Windows (toutes les minutes) → GET api/print_worker.php?key=SECRET
→ Traite tous les jobs pending restants (FIFO, max 20 par appel)
→ Retry jusqu'à max_attempts (5 par défaut)
Fichiers clés :
| Fichier | Rôle |
|---|---|
public/api/escpos.php |
Primitives ESC/POS (commandes, iconv CP858, fsockopen) |
public/api/tickets_escpos.php |
Builders de tickets (accueil, pizzaiolo, cuisine) |
public/api/print_queue.php |
Helpers queue : enqueue, drain, worker, send |
public/api/print_worker.php |
Endpoint worker (auth par secret) |
public/api/bridge_print.php |
Push immédiat via queue |
public/api/bridge_status.php |
Ping TCP imprimante + stats queue |
sql/migrations/2026_05_14_print_jobs.sql |
Migration DB (table print_jobs) |
sql/migrations/2026_05_16_drop_bridge_columns.sql |
Suppression colonnes bridge legacy |
Migrations DB (à faire une fois, dans l'ordre)
-- Via phpMyAdmin ou mysql CLI :
SOURCE sql/migrations/2026_05_14_print_jobs.sql;
SOURCE sql/migrations/2026_05_16_drop_bridge_columns.sql;
Configuration imprimante
Depuis la page admin : public/admin/printer_settings.html
- IP imprimante : adresse IP de l'imprimante réseau (ex:
192.168.1.100) - Port :
9100(défaut Star RAW) - Bouton "Status" : ping TCP live + stats queue
- Bouton "Tester l'impression" : envoie un ticket de test fictif directement à l'imprimante
Cron Windows (Task Scheduler)
Créer une tâche planifiée toutes les minutes :
- Programme :
curl - Arguments :
"http://localhost/api/print_worker.php?key=VOTRE_SECRET"- Remplacer
VOTRE_SECRETpar la valeur affichée dans la page admin imprimante localhost= XAMPP local
- Remplacer
Ou via PowerShell (test manuel) :
curl "http://localhost/api/print_worker.php?key=VOTRE_SECRET"
Réponse attendue :
{"ok":true,"stats":{"processed":2,"ok":2,"failed":0},"limit":20,"ts":"..."}
Monitoring (page admin)
Ouvrir public/admin/printer_settings.html :
- Bouton "Status" : ping TCP imprimante + stats queue (pending / failed / done)
- Bouton "Lancer le worker" : déclenche le worker manuellement
- Bouton "Réessayer les failed" : remet les jobs failed en pending (attempts=0)
- Worker Secret : affiché en lecture seule, bouton "Régénérer" disponible
- Réimprimer un ticket : depuis
public/admin/orders.html, bouton "Réimprimer"
Debug SQL (lire la queue directement)
-- Jobs en attente ou échoués
SELECT id, order_id, ticket_kind, status, attempts, max_attempts, last_error, created_at
FROM print_jobs
WHERE status IN ('pending','failed')
ORDER BY created_at DESC
LIMIT 50;
-- Stats globales
SELECT status, COUNT(*) AS cnt FROM print_jobs GROUP BY status;
-- Forcer un retry sur tous les failed
UPDATE print_jobs SET status='pending', attempts=0, last_error=NULL WHERE status='failed';
Logique retry
| Cas | Comportement |
|---|---|
| Erreur réseau / timeout | pending (attempts++) — réessayé par le worker |
| Imprimante injoignable | pending — réessayé par le worker |
attempts >= max_attempts |
Ignoré par le worker (reste failed) |
| Bouton "Réessayer" admin | Remet attempts=0, status=pending |
| Job legacy (sans escpos_b64) | failed immédiat, pas de crash |
Note: public/api/config.php existe toujours comme fallback IP/port pour les fresh installs.
Security note
No auth is implemented. Protect /admin and /api/admin before using in production.