Prerequisiti
| Risorsa | Minimo | Consigliato |
|---|---|---|
| CPU | 4 vCPU | 8 vCPU |
| RAM | 8 GB | 16 GB |
| Disco | 40 GB SSD | 100 GB SSD |
| OS | Ubuntu 22.04 LTS / Debian 12 | |
| Docker | ≥ 24.x + Compose v2 | |
ss -tlnp | grep 308.Server & Docker
# Installa Docker su Ubuntu apt update && apt install -y ca-certificates curl gnupg git jq curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ gpg --dearmor -o /etc/apt/keyrings/docker.gpg apt install -y docker-ce docker-ce-cli docker-compose-plugin # Clone repository git clone https://git.certisource.it/AdminGit2026/wms-agile.git /var/www/wms-agile # CRITICO: imposta nome progetto (evita conflitti Docker) echo "COMPOSE_PROJECT_NAME=wms-agile" > /var/www/wms-agile/infrastructure/.env
Per la guida completa di installazione server → docs/INSTALL.html
Configurazione file di ambiente
# Copia template per ogni servizio for svc in auth inventory warehouse inbound outbound report ai calendar wcs network; do cp services/${svc}-ms/.env services/${svc}-ms/.env.production done # JWT_SECRET uguale per tutti (CRITICO) JWT=$(openssl rand -hex 32) for svc in auth inventory warehouse inbound outbound report ai calendar wcs network; do sed -i "s/^JWT_SECRET=.*/JWT_SECRET=${JWT}/" services/${svc}-ms/.env.production done
services/ai-ms/.env.production. Health check conferma: "anthropic_key":"configured","model":"claude-sonnet-4-6". Ogni tenant può sovrascriverla con la propria chiave via Admin → AI / RAG.wms_kb_{tenant_id}. Indicizzazione aggiuntiva: Admin → AI / RAG → "Indicizza Documentazione WMS" (90 secondi).Avvio & verifica
# Build e start (prima volta ~15 min) cd /var/www/wms-agile/infrastructure COMPOSE_PROJECT_NAME=wms-agile docker compose \ -f docker-compose.yml -f docker-compose.production.yml up -d --build # Verifica health check (attendi ~60s per MySQL) for port in 3081 3082 3083 3084 3085 3086 3087; do echo "Porta $port: $(curl -sk -o /dev/null -w '%{http_code}' http://127.0.0.1:$port/health)" done
sql/06-*.sql → sql/19-*.sql dopo il primo avvio. Sono idempotenti (IF NOT EXISTS).Primo tenant (onboarding)
./infrastructure/first-install.sh \ https://wms.tuodominio.it \ "Nome Azienda" admin@azienda.it "Password2026!" # Output atteso: 12/12 ✅ (tenant + admin + warehouse + 6 zone + 3 prodotti)
Apache reverse proxy + SSL
<VirtualHost *:443>
ServerName wms.tuodominio.it
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/wms.tuodominio.it/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/wms.tuodominio.it/privkey.pem
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3080/
ProxyPassReverse / http://127.0.0.1:3080/
# SSE streaming AI: no buffering
ProxyPassMatch ^/api/ai/chat$ http://127.0.0.1:3080/api/ai/chat
Header always set Cache-Control "no-cache"
</VirtualHost>certbot --apache -d wms.tuodominio.it a2ensite wms-agile wms-agile-ssl && systemctl reload apache2
Algoritmi di Prelievo (Picking) Aggiornato
Il picking è la fase critica dell'outbound: determinare quale unità fisica prelevare quando ci sono più lotti/ubicazioni disponibili dello stesso SKU. La scelta dell'algoritmo impatta direttamente su rotazione stock, scaduti, produttività operatori e costi di movimentazione.
picking_strategy nella tabella wms_inventory_db.product_config. È possibile combinare più strategie con priorità.Algoritmi disponibili
Il primo prodotto entrato in magazzino è il primo ad essere prelevato. Basato sulla data di ricevimento del lotto.
ORDER BY sl.received_at ASC,
sl.lot_number ASCIl lotto con la data di scadenza più vicina viene prelevato per primo, indipendentemente dall'ordine di ingresso. Richiede gestione lotti con expiry_date.
ORDER BY l.expiry_date ASC,
l.manufactured_date ASCL'ultimo prodotto entrato è il primo ad essere prelevato. Usato in casi specifici dove i prodotti più recenti sono fisicamente più accessibili.
ORDER BY sl.received_at DESC
Il magazzino è diviso in zone. Ogni operatore copre solo la propria zona. Gli ordini multi-zona vengono consolidati alla fine del processo.
zone_id = operators.assigned_zone picking_mode = 'zone_based' merge_timeout_min = 15
Gli ordini vengono raggruppati in "onde" (wave) e rilasciati in batch ottimizzati. Permette di pianificare risorse, AGV e dock di spedizione in anticipo.
POST /api/outbound/wave/create
{ wave_size: 50, ship_window: "14:00",
strategy: "minimize_travel" }Un operatore preleva articoli per più ordini in un unico giro con un carrello multi-tote. Ciascun tote rappresenta un ordine. Maximizza la produttività per ordine.
cluster_size = 4 # tote per giro
sort_by = "zone_proximity"Raggruppa ordini che condividono gli stessi SKU. L'operatore preleva la quantità totale per tutti gli ordini del batch, poi smista al sorter.
batch_mode = 'same_sku' batch_size_max = 20 sort_method = 'conveyor'
Ordina i prelievi per minimizzare la distanza totale percorsa dall'operatore/AGV usando una sequenza ottimizzata per corsia → scaffale → ripiano.
ORDER BY zone_code ASC,
aisle_code ASC,
shelf_level ASC,
bin_position ASCTabella comparativa — quale algoritmo scegliere?
| Algoritmo | Scadenze | Prod. | Comp. | Ideale per |
|---|---|---|---|---|
| FIFO | No | Alta | Bassa | Standard — tutti i settori |
| FEFO | Sì ✓ | Alta | Media | Pharma, Food, Cosmetica |
| LIFO | No | Media | Bassa | Materiali non deperibili |
| Zone | Combinabile | Molto alta | Media | Magazzini >5.000 mq |
| Wave | Combinabile | Molto alta | Alta | AGV, finestre spedizione fisse |
| Cluster | Combinabile | Eccellente | Media | E-commerce ordini piccoli |
| Batch | No | Eccellente | Alta | E-commerce con sorter |
| Proximity | Combinabile | Ottima | Alta | AGV, ottimiz. energetica |
Algoritmi di Deposito (Put-Away) Aggiornato
Il put-away determina dove collocare la merce appena ricevuta. Un algoritmo corretto riduce il numero di movimenti durante il picking, ottimizza l'utilizzo dello spazio e garantisce la conformità (temperatura, normative, separazione merci).
putaway_strategy nella configurazione tenant/categoria. Il sistema può combinare più strategie con una catena di priorità (rule chain): prima verifica la regola 1, se non applicabile passa alla 2, ecc.Algoritmi disponibili
Ad ogni SKU è assegnata una o più ubicazioni fisse. La merce va sempre e solo in quel bin. Semplice, prevedibile, facile da ricordare per gli operatori.
putaway_strategy = 'fixed' primary_bin = products.default_bin_code
I prodotti A (più movimentati) vanno in posizioni ergonomiche vicino alla spedizione. I prodotti C (lenta rotazione) vanno in zone remote o ad altezze difficili.
CASE products.abc_class WHEN 'A' THEN 'ZONA-PICK-FRONT' WHEN 'B' THEN 'ZONA-PICK-MID' WHEN 'C' THEN 'ZONA-BULK-REAR' END
Abbina il prodotto alla zona più adatta in base a: temperatura richiesta, peso, categoria merceologica, tenant, normative (GDP, HACCP, ADR).
PharmaNet + requires_cold → zona GDP (2-8°C) categoria = 'pericoloso' → zona ADR separata tenant_id = RetailDistrib → zona ecommerce
La merce viene collocata nella prima ubicazione libera disponibile nelle zone ammissibili. Massimizza l'utilizzo dello spazio ma richiede picking proximity-based.
WHERE b.status = 'empty' AND b.zone_id IN (allowed_zones) ORDER BY b.distance_from_dock ASC LIMIT 1
I pallet completi vanno in zona riserva (bulk). Quando il bin di picking attivo si svuota sotto soglia, viene generata automaticamente una missione di rifornimento.
IF bin.qty < replenishment_threshold:
CREATE mission(type='replenishment',
from=bulk_bin, to=active_bin)La merce in arrivo è già allocata ad ordini in uscita. Viene portata direttamente all'area staging/spedizione senza essere stoccata. Tempo di permanenza minimo.
IF inbound_line.pre_allocated_order: putaway_zone = 'STAGING' skip_storage = TRUE
Tabella comparativa — quale algoritmo scegliere?
| Algoritmo | Spazio | Velocità dep. | Comp. | Ideale per |
|---|---|---|---|---|
| Fixed | Non ottimale | Alta | Bassa | Catalogo stabile, magazzini piccoli |
| ABC Slotting | Media | Media | Media | Ampio assortimento, Pareto tipico |
| Zone-Based | Media | Alta | Media | Pharma GDP, multi-tenant, ADR |
| Random | Massimo | Alta | Bassa | Catalogo variabile + WMS avanzato |
| Bulk-to-Active | Ottimale | Media | Alta | SKU alta rotazione, AGV disponibili |
| Cross-Docking | Zero | Massima | Alta | Freschi, transito, pre-allocazione |
AI / RAG Knowledge Base Live da Feb 2026
Il sistema RAG (Retrieval-Augmented Generation) è attivo in produzione. La chat AI Claude non risponde solo con conoscenza generica — consulta la knowledge base aziendale del tenant prima di rispondere. SOP, procedure operative, guide dei moduli WMS: tutto indicizzato in vettori semantici e ricercato in tempo reale.
• Voyage AI (voyage-3-lite, 512 dim, cosine similarity) → embedding semantici
• Qdrant (Docker container wms-qdrant:6333) → vector DB isolato per tenant
• 15 moduli WMS pre-indicizzati · 36 chunk · collection
wms_kb_1• Soglia rilevanza: score cosine ≥ 0.35 · top-4 chunk per query
Come indicizzare i propri documenti
Dal pannello Admin → Impostazioni → AI / RAG sono disponibili due modalità:
# 1. Auto-seed moduli WMS (15 guide operative, ~90 secondi) POST /api/ai/knowledge/seed-wms # → indicizza Dashboard, Inbound, Outbound, Missioni, 4PL, TMS, RAG, WCS, Rete Filiera... # 2. Indicizza documento custom (SOP aziendale, manuale fornitore, policy) POST /api/ai/knowledge/ingest Content-Type: application/json { "doc_id": "sop-gdp-2026-003", "title": "SOP GDP — Gestione Farmaci 2-8°C", "content": "Procedura operativa: i farmaci refrigerati vanno...", "source": "wms://sop/gdp/003" } # 3. Verifica statistiche knowledge base GET /api/ai/knowledge/stats # → { "points_count": 36, "status": "green", "collection": "wms_kb_1" }
Regole AI tramite Prompt Opzione Futura — Q3 2026
Evoluzione pianificata: definire regole di picking e put-away in linguaggio naturale, interpretate da Claude AI e tradotte in configurazione strutturata. L'infrastruttura Claude + RAG è già attiva — manca solo il tool set_warehouse_rules.
ai-ms con Claude Sonnet è già attivo e gestisce 20+ tool WMS. La funzionalità "regole AI" è pianificata come estensione del sistema esistente di tool-calling. Non richiede infrastruttura aggiuntiva — solo l'implementazione del tool set_warehouse_rules.Mockup interfaccia
Nell'area di configurazione del supervisore, un campo prompt permette di descrivere le regole in italiano:
"rules": [ { "id": "pharmanet-gdp", "name": "PharmaNet → GDP", "priority": 1, "trigger": { "tenant": "PharmaNet", "category": "farmaco" }, "action": { "putaway_zone": "GDP", "temp_range_c": [2, 8], "compliance": "EU-GDP-2013" } }, { "id": "retail-flash-sale-lifo", "name": "RetailDistrib Flash Sale → LIFO", "priority": 2, "trigger": { "tenant": "RetailDistrib", "event": "flash_sale" }, "action": { "picking_strategy": "LIFO", "reason": "velocità massima su event sale" } }, { "id": "abc-a-min-bins", "name": "Classe A — 2 bin attivi minimi", "priority": 3, "trigger": { "abc_class": "A" }, "action": { "min_active_bins": 2, "zone_proximity": "near_shipping", "trigger_replenishment_at": "30%" } } ]
Esempi di prompt supportati
Roadmap implementazione
Aggiunta del tool set_warehouse_rules(rules_json) all'ai-ms. Claude può già interpretare linguaggio naturale e convertirlo in JSON strutturato.
Interfaccia textarea nella sezione Configurazione → Regole WMS. Preview degli effetti prima dell'applicazione.
Libreria di template pre-validati (GDP, HACCP, ADR), versioning delle regole con rollback, diff visuale.
Claude analizza le metriche operative (distanza media picking, stockout rate, scaduti) e propone autonomamente modifiche alle regole. L'admin approva o rifiuta.
Aggiornamento
# Standard: pull + rebuild + sql cd /var/www/wms-agile && git pull origin main cd infrastructure COMPOSE_PROJECT_NAME=wms-agile docker compose \ -f docker-compose.yml -f docker-compose.production.yml up -d --build ./deploy-hetzner.sh --verify # Hot-fix PHP (immediato, non persiste al restart) docker cp services/inbound-ms/src/Services/InboundService.php \ wms-inbound-ms:/var/www/services/inbound-ms/src/Services/ docker exec wms-inbound-ms kill -USR2 1
git pull fare sempre docker restart wms-gateway: git crea nuovi inode, il container vede ancora i vecchi file.Troubleshooting
Container non parte
docker logs wms-{svc}-ms --tail=50 docker inspect wms-mysql | grep -A5 '"Health"'
Login 401
# Verifica JWT_SECRET allineato for svc in auth inventory warehouse; do echo -n "$svc: "; grep JWT_SECRET services/${svc}-ms/.env.production done # Se disallineati: riavvia TUTTI docker restart $(docker ps --filter name=wms --format "{{.Names}}")
AI chat non risponde
docker exec wms-ai-ms env | grep ANTHROPIC_API_KEY docker logs wms-ai-ms --tail=20
Picking estratto lotto sbagliato
Se il picking non rispetta FEFO, verificare che:
- I lotti abbiano
expiry_datepopolato inwms_inventory_db.lots - La configurazione SKU abbia
picking_strategy = 'FEFO' - La query OutboundService usi il JOIN su
lots.expiry_datenel ORDER BY
Auto-Learn & Cron Job
Il sistema AI si arricchisce automaticamente dai dati operativi live. Ogni notte alle 03:00 UTC un cron job genera 5 snapshot dal database e li indicizza in Qdrant via Voyage AI.
Cron Configuration
# File: /etc/cron.d/wms-ai-autolearn 0 3 * * * root curl -s -X POST http://127.0.0.1:3087/api/ai/knowledge/auto-learn \ -H "X-API-Key: YOUR_API_KEY" >> /var/log/wms-ai-autolearn.log 2>&1
Documenti Generati
| doc_id | Database | Contenuto |
|---|---|---|
wms-live-stock | inventory | Top 15 prodotti + scorte sotto punto di riordino |
wms-live-alerts | warehouse | Alert attivi per severity + risolti ultime 48h |
wms-live-warehouse | warehouse | Zone, bin, operatori attivi, missioni in corso |
wms-live-billing | agile | Fatture per status con totali EUR |
wms-live-ncr | warehouse | Non conformità aperte |
Esecuzione Manuale
# Trigger manuale (es. dopo import dati massivo) curl -s -X POST http://127.0.0.1:3087/api/ai/knowledge/auto-learn \ -H "X-API-Key: YOUR_API_KEY" | python3 -m json.tool # Risposta attesa: {"success":true,"data":{"indexed":5,"total":5}}
Inventario Ciclico
L'inventario ciclico verifica lo stock in modo continuativo senza fermare le operazioni. Il servizio CycleCountService in inventory-ms gestisce pianificazione, esecuzione e reporting.
Schema Database
-- wms_inventory_db CREATE TABLE cycle_counts ( id INT AUTO_INCREMENT PRIMARY KEY, tenant_id INT NOT NULL, code VARCHAR(30) UNIQUE, type ENUM('full','zone','spot') DEFAULT 'full', zone_id INT NULL, -- NULL = full warehouse status ENUM('planned','in_progress','completed','cancelled'), scheduled_date DATE, completed_at TIMESTAMP NULL, operator_id INT NULL, notes TEXT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE cycle_count_lines ( id INT AUTO_INCREMENT PRIMARY KEY, cycle_count_id INT NOT NULL, product_id INT NOT NULL, bin_code VARCHAR(30), expected_qty DECIMAL(12,2), -- sistema counted_qty DECIMAL(12,2) NULL, -- operatore variance DECIMAL(12,2) NULL, counted_at TIMESTAMP NULL, FOREIGN KEY (cycle_count_id) REFERENCES cycle_counts(id) );
API Endpoints
| Metodo | Endpoint | Descrizione |
|---|---|---|
GET | /api/cycle-counts | Lista conteggi con filtri (status, type, date) |
POST | /api/cycle-counts | Crea conteggio pianificato (full/zone/spot) |
POST | /api/cycle-counts/quick | Conteggio rapido singolo bin |
WCS / MQTT Bridge
Il microservizio wcs-ms (porta 3089) fa da bridge tra WMS e i sistemi automatici (AGV, sorter, nastri) via protocollo MQTT (Mosquitto, porta 1883).
Architettura
WMS App ──→ wcs-ms (REST API) ──→ Mosquitto (MQTT broker) ──→ AGV/PLC ↑ │ └────── SSE events ◄─── subscribe ◄──────┘
MQTT Topics
| Topic | Dir | Payload |
|---|---|---|
wms/agv/{id}/status | AGV→WMS | battery, position, state (idle/busy/charging) |
wms/agv/{id}/mission | WMS→AGV | mission_id, source_bin, target_bin, priority |
wms/agv/{id}/position | AGV→WMS | x, y, zone_code (per mappa SVG) |
wms/agv/{id}/complete | AGV→WMS | mission_id, status (completed/failed), timestamp |
Container
# Verifica MQTT broker docker logs wms-mosquitto --tail=20 # Test publish docker exec wms-mosquitto mosquitto_pub -t "wms/agv/AGV-01/status" \ -m '{"battery":85,"state":"idle","zone":"PICK-A"}' # WCS health curl -s http://127.0.0.1:3089/health | python3 -m json.tool
Notifiche SSE (Server-Sent Events)
Le notifiche push usano SSE per delivery real-time al browser. L'endpoint /api/realtime (warehouse-ms) mantiene una connessione persistente HTTP con Content-Type: text/event-stream.
Configurazione Nginx per SSE
# CRITICO: proxy_http_version 1.1 SOLO nei location SSE # A livello server block causa 405 per tutti i POST! location ~ ^/api/realtime { proxy_pass http://warehouse_service; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Connection ''; proxy_buffering off; proxy_cache off; proxy_read_timeout 130s; chunked_transfer_encoding on; }
Severity & Canali
| Severity | In-App | SMS/Webhook | |
|---|---|---|---|
| info | Badge | — | — |
| warning | Badge + Toast | Opzionale | — |
| critical | Toast auto | Sempre | Opzionale |
| emergency | Toast + Sound | Sempre | Sempre |
PWA & Service Worker
L'app operatore (operator.html) e il palmare (mobile.html) sono PWA installabili con Service Worker per caching offline.
Service Worker (app/sw.js)
// Strategia: cache-first per shell, network-only per API const CACHE_NAME = 'wms-op-v2'; const SHELL_ASSETS = [ '/operator.html', '/mobile.html', '/css/mobile.css', '/js/mobile.js', '/js/api.js', 'https://cdnjs.cloudflare.com/.../all.min.css' ];
Installazione su Device
| Piattaforma | Procedura |
|---|---|
| Android (Chrome) | Menu ⋮ → "Aggiungi a schermata Home" → Installa |
| iOS (Safari) | Condividi ↑ → "Aggiungi a Home" → Aggiungi |
| Zebra/Honeywell | Usare mobile.html (118 righe, ultraleggero) per schermi piccoli e connessioni lente |
CACHE_NAME (es. wms-op-v3) per forzare l'aggiornamento della cache su tutti i device. Il vecchio SW viene invalidato automaticamente.Backup & Disaster Recovery
Backup Database (7 DB)
# Backup completo tutti i 7 DB WMS for db in wms_auth_db wms_inventory_db wms_warehouse_db wms_inbound_db wms_outbound_db wms_report_db wms_agile_db; do docker exec wms-mysql mysqldump -u wms_user -pWmsAgile2026! $db | \ gzip > /backup/wms/${db}_$(date +%Y%m%d_%H%M).sql.gz done # Retention: 30 giorni find /backup/wms -name "*.sql.gz" -mtime +30 -delete
Backup Qdrant (RAG vectors)
# Snapshot Qdrant curl -X POST http://127.0.0.1:6333/snapshots # Copia snapshot dal volume Docker docker cp wms-qdrant:/qdrant/snapshots/ /backup/wms/qdrant/
Disaster Recovery
# 1. Restore database gunzip -c /backup/wms/wms_inventory_db_20260325.sql.gz | \ docker exec -i wms-mysql mysql -u wms_user -pWmsAgile2026! wms_inventory_db # 2. Restart tutti i container cd /var/www/wms-agile/infrastructure COMPOSE_PROJECT_NAME=wms-agile docker compose \ -f docker-compose.yml -f docker-compose.production.yml up -d # 3. Re-indicizza RAG (i dati DB sono aggiornati, i vector devono seguire) curl -X POST http://127.0.0.1:3087/api/ai/knowledge/auto-learn \ -H "X-API-Key: YOUR_API_KEY" curl -X POST http://127.0.0.1:3087/api/ai/knowledge/seed-wms \ -H "X-API-Key: YOUR_API_KEY"