Skip to Content
TecnicaFunzionalitaSistema di Gestione Inventario

Sistema di Gestione Inventario

Panoramica

Il sistema di gestione inventario include due funzionalità principali:

  1. Inventario Fisico: Permette di registrare i conteggi fisici del magazzino e confrontarli con le quantità stimate
  2. Aggiornamento Automatico Giacenze: Aggiorna automaticamente le giacenze stimate in base alle vendite dei prodotti finiti

Inventario Fisico

Come funziona

  1. Vai su MagazzinoNuovo Inventario
  2. Seleziona i prodotti da conteggiare dalla lista a sinistra
  3. Inserisci la quantità fisica contata per ogni prodotto
  4. Il sistema calcola automaticamente le varianze rispetto alle quantità stimate
  5. Salva lo snapshot dell’inventario

Cosa viene salvato

  • Data e ora del conteggio
  • Chi ha effettuato il conteggio (opzionale)
  • Note (opzionale)
  • Per ogni prodotto:
    • Quantità fisica contata
    • Quantità stimata dal sistema
    • Varianza (differenza)
    • Varianza percentuale
    • Valore della varianza in €

Visualizzazione Storico

Vai su MagazzinoStorico Inventari per vedere tutti gli inventari effettuati.

Ogni snapshot mostra:

  • Data e operatore
  • Numero di prodotti contati
  • Varianza totale in valore (€)
  • Dettaglio per ogni prodotto (espandibile)

Aggiornamento Automatico Giacenze da Vendite

Come funziona

Quando vengono venduti prodotti finiti attraverso Cassa in Cloud, il sistema:

  1. Identifica il prodotto finito venduto tramite il cassaincloudmapping
  2. Legge la ricetta del prodotto finito
  3. Per ogni ingrediente nella ricetta:
    • Calcola la quantità consumata (quantità per porzione × quantità vendute)
    • Converte l’unità di misura se necessario
    • Calcola quante “unità di acquisto” sono state consumate
    • Aggiorna la giacenza stimata sottraendo la quantità consumata

Esempio

Prodotto Finito: Carbonara (vendute 10 porzioni)

Ricetta:

  • 100g guanciale per porzione
  • 50g pecorino per porzione
  • 2 uova per porzione

Prodotti Acquistati:

  • Guanciale: comprato 1000g per confezione, 5 confezioni in magazzino
  • Pecorino: comprato 500g per confezione, 3 confezioni in magazzino
  • Uova: comprato 12 pz per confezione, 2 confezioni in magazzino

Calcolo Consumo:

  • Guanciale: 100g × 10 = 1000g = 1 confezione → giacenza stimata: 5 - 1 = 4 confezioni
  • Pecorino: 50g × 10 = 500g = 1 confezione → giacenza stimata: 3 - 1 = 2 confezioni
  • Uova: 2 × 10 = 20 pz = 1.67 confezioni → giacenza stimata: 2 - 1.67 = 0.33 confezioni

Integrazione con Cassa in Cloud (Cassanova)

Il sistema è già configurato per integrarsi con l’API di Cassa in Cloud (https://api.cassanova.com ).

Configurazione Esistente:

  • API Client: /src/store/api/cassaincloudApi.ts
  • API Proxy: /src/app/api/cassa-in-cloud/route.ts
  • API Key: NEXT_PUBLIC_CASSAINCLOUD_API_KEY (variabile d’ambiente)

Opzione 1: Webhook (Consigliato)

Configura un webhook in Cassa in Cloud per chiamare il nostro endpoint ogni volta che viene effettuata una vendita:

Endpoint: POST /api/inventory/update-from-sales

Payload:

{ "sales": [ { "product_id": "CASSAINCLOUD_MAPPING_ID", "quantity": 10 } ] }

Configurazione Webhook su Cassa in Cloud:

  1. Accedi a MyCassa in Cloud → Impostazioni → Webhooks
  2. Crea un nuovo webhook per l’entità “Receipt” o “Bill”
  3. URL: https://tuodominio.com/api/inventory/update-from-sales
  4. Eventi: create, edit

Opzione 2: Sincronizzazione Automatica Giornaliera con BullMQ (✅ IMPLEMENTATO)

Il sistema include un job BullMQ che sincronizza automaticamente le vendite una volta al giorno alle 2:00 AM.

Attivazione:

  1. Tramite UI: Vai su MagazzinoSincronizza VenditeAttiva Sync Automatica

  2. Tramite API:

// POST /api/inventory/sync-sales { "action": "schedule" }
  1. Tramite Codice:
import { scheduleDailySalesSync } from "@/jobs/syncSalesJob"; await scheduleDailySalesSync(tenantId);

Come funziona:

  • Il job viene eseguito ogni giorno alle 2:00 AM
  • Recupera le vendite del giorno precedente da Cassa in Cloud
  • Aggiorna automaticamente le giacenze stimate per ogni ingrediente
  • Registra i risultati per monitoraggio

Sincronizzazione Manuale:

Puoi anche triggerare una sincronizzazione manuale:

  1. Tramite UI: MagazzinoSincronizza VenditeSincronizza Ora (Ieri)

  2. Tramite API:

// POST /api/inventory/sync-sales { "action": "trigger", "date": "2025-11-25" // opzionale, default: ieri }

Monitoraggio:

Controlla lo stato della sincronizzazione:

// GET /api/inventory/sync-sales // Ritorna: // - scheduled: job schedulati // - active: job in esecuzione // - recent_completed: ultimi job completati // - recent_failed: ultimi job falliti

Opzione 3: Sincronizzazione Manuale

Crea un pulsante nella dashboard che permette di sincronizzare manualmente le vendite:

const handleSyncSales = async () => { // Ottieni le vendite dalle ultime 24 ore da Cassa in Cloud const sales = await fetchRecentSales(); await fetch("/api/inventory/update-from-sales", { method: "POST", body: JSON.stringify({ sales }), }); };

API Endpoints

POST /api/inventory/snapshots

Crea un nuovo snapshot dell’inventario fisico.

Body:

{ "items": [ { "product_id": "PRODUCT_ID", "physical_count": 5.5 } ], "notes": "Inventario mensile", "performed_by": "Mario Rossi" }

Response:

{ "success": true, "snapshot": { "_id": "SNAPSHOT_ID", "snapshot_date": "2025-11-26T10:00:00.000Z", "total_variance_value": -150.50, "items": [...] } }

GET /api/inventory/snapshots

Recupera lo storico degli snapshot.

Query params:

  • limit: numero massimo di risultati (default: 10)
  • skip: numero di risultati da saltare (default: 0)

Response:

{ "snapshots": [...], "total": 25, "limit": 10, "skip": 0 }

POST /api/inventory/update-from-sales

Aggiorna le giacenze stimate in base alle vendite.

Body:

{ "sales": [ { "product_id": "CASSAINCLOUD_MAPPING_ID", "quantity": 10 } ] }

Response:

{ "success": true, "processed": 1, "results": [ { "product_id": "CASSAINCLOUD_MAPPING_ID", "product_name": "Carbonara", "quantity_sold": 10, "success": true, "ingredient_updates": [ { "product_id": "INGREDIENT_ID", "product_name": "Guanciale", "consumed_units": 1.0, "old_estimate": 5, "new_estimate": 4 } ] } ] }

Best Practices

  1. Inventario Fisico Regolare: Esegui un inventario fisico almeno una volta al mese
  2. Confronta le Varianze: Analizza le varianze per identificare:
    • Sprechi non registrati
    • Errori nelle ricette
    • Furti o perdite
    • Errori nei conteggi di acquisto
  3. Mantieni le Ricette Aggiornate: Assicurati che le ricette riflettano le quantità effettivamente utilizzate
  4. Monitora le Giacenze Stimate: Se le giacenze stimate diventano negative o molto diverse dalla realtà, potrebbe indicare problemi nelle ricette o nei consumi

File Importanti

/src/jobs/syncSalesJob.ts

Contiene il worker BullMQ per la sincronizzazione automatica delle vendite:

Funzioni principali:

  • scheduleDailySalesSync(tenantId): Schedula la sincronizzazione automatica giornaliera
  • triggerManualSalesSync(tenantId, date?): Triggera una sincronizzazione manuale
  • syncSalesQueue: La queue BullMQ per i job di sincronizzazione
  • syncSalesWorker: Il worker che processa i job

Workflow del Worker:

  1. Riceve un job con tenantId e date (opzionale)
  2. Calcola la data target (default: ieri)
  3. Chiama /api/cassa-in-cloud/sales?date=YYYY-MM-DD
  4. Ottiene le vendite da Cassa in Cloud
  5. Chiama /api/inventory/update-from-sales con le vendite
  6. Aggiorna le giacenze stimate
  7. Ritorna il risultato con statistiche

Configurazione:

  • Schedule: Ogni giorno alle 2:00 AM (0 2 * * *)
  • Retry: 3 tentativi con backoff esponenziale
  • Concurrency: 1 (per evitare conflitti)

/src/app/api/inventory/sync-sales/route.ts

Endpoint API per gestire la sincronizzazione:

Actions:

  • schedule: Attiva la sincronizzazione automatica giornaliera
  • trigger: Esegue una sincronizzazione manuale immediata
  • status: Ottiene lo stato di tutti i job
  • cancel: Cancella la sincronizzazione schedulata

/src/app/api/cassa-in-cloud/sales/route.ts

Endpoint che recupera le vendite dall’API reale di Cassa in Cloud.

Integrazione con API Cassanova:

  • Utilizza il report “Sold by Product” (/reports/sold/products)
  • Chiama l’API proxy locale che gestisce l’autenticazione OAuth2
  • Trasforma i dati dal formato Cassa in Cloud al formato interno
  • Filtra per data e punto vendita

Parametri:

  • date: Data in formato YYYY-MM-DD (richiesto)
  • idsSalesPoint: IDs dei punti vendita separati da virgola (opzionale)

Flusso:

  1. Valida i parametri della richiesta
  2. Costruisce la query per il report “Sold by Product” di Cassa in Cloud
  3. Chiama /api/cassa-in-cloud?endpoint=reports/sold/products&...
  4. Il proxy /api/cassa-in-cloud/route.ts gestisce:
    • Autenticazione con API key (POST /apikey/token)
    • Caching del token di accesso
    • Chiamata all’API Cassanova con Bearer token
  5. Trasforma la risposta nel formato atteso dall’inventario
  6. Restituisce le vendite con totali

Variabili d’Ambiente Richieste:

  • NEXT_PUBLIC_CASSAINCLOUD_API_KEY: API key fornita da Cassa in Cloud
  • NEXT_PUBLIC_BASE_URL: URL base dell’applicazione (per chiamate interne)

Risposta di Esempio:

{ "success": true, "date": "2025-11-25", "sales": [ { "product_mapping_id": "CASSACLOUD_PRODUCT_ID", "product_name": "Carbonara", "quantity_sold": 15, "total_amount": 180.0, "sales_point_id": 1, "timestamp": "2025-11-25" } ], "total_sales": 35, "total_amount": 408.0, "total_quantity": 35 }

Modelli Database

InventorySnapshot

{ snapshot_date: Date, items: [{ product_id: ObjectId, product_name: string, physical_count: number, estimated_count: number, variance: number, variance_percentage: number, unit: string, value_at_count: number }], total_variance_value: number, notes: string, performed_by: string, tenant_id: ObjectId }

Product (campi rilevanti per inventario)

{ quantity: number, // Quantità acquistata per unità buyUnit: string, // Unità di acquisto (es: "kg", "pz") stockEstimate: number, // Giacenza stimata (numero di unità di acquisto) wastePercentage: number, // Percentuale di scarto // ... altri campi }
Last updated on