FireFeed Docs

Architettura OMS

Runtime, isolamento tenant, permessi, superficie API, idempotenza e outbox dell'OMS.

L'OMS FireFeed è un modulo TypeScript/Next.js integrato nella piattaforma, non un servizio o una dashboard separata. Gestisce il ciclo operativo successivo all'acquisizione dell'ordine e delega gli effetti remoti affidabili ai worker tramite outbox.

Questa pagina è il catalogo tecnico manuale dell'API OMS. Il generatore OpenAPI del repository copre attualmente soltanto le definizioni del Feed Manager web e del worker; le route packages/oms/app/api/** non entrano ancora negli spec generati.

Runtime e proprietà dei dati

Browser / client OMS
        |
        v
Next.js OMS: pagine + route API
        |--- schema pubblico: identità, company, entitlement, progetti, connessioni
        |--- schema oms_<companyId>: ordini, stock, spedizioni, resi, eventi, outbox
        |
        +--- commit dominio + outbox --- BullMQ (wake-up/scheduling) ---> worker
                                                                    |       |
                                                                    v       v
                                                              provider   operations

PostgreSQL è la fonte di verità. BullMQ trasporta il segnale di lavoro e la pianificazione, ma non sostituisce lo stato persistente dell'evento.

Ogni richiesta autenticata risolve sessione, company attiva, entitlement al modulo OMS e permesso di lettura dell'area. Alla prima necessità lo schema tenant oms_<companyId> viene creato o verificato. I dati dinamici di prodotto del Feed Manager restano invece nello schema company_<companyId>.

Disponibilità dei provider

La sola esistenza di un adapter non rende disponibile una connessione. Il resolver combina:

  • implementazione e stato di readiness (ready, controlled, beta, blocked);
  • entitlement del tenant e feature flag o allowlist;
  • capability dichiarate dal provider;
  • capability effettive della connessione.

Le API di creazione non permettono di attivare subito un canale. L'attivazione richiede un connection check riuscito e ancora valido; modificare configurazione o API key disabilita il canale e invalida l'health precedente.

Autorizzazione e capability

La lettura richiede accesso all'area OMS. Ogni mutazione richiede anche il permesso write; il ruolo o la capability non possono aggirare un'area in sola lettura.

CapabilityRuolo minimoOperazione protetta
order.confirmMemberConferma ordine
order.shipMemberSpedizione ordine
order.send_supplierMemberInvio al fornitore
order.editAdminModifica ordine
order.cancelAdminAnnullamento ordine
return.receiveMemberRicezione reso
return.approveAdminApprovazione reso
return.rejectAdminRifiuto reso
return.refundAdminRimborso
return.configure_portalAdminConfigurazione portale resi
inventory.configureAdminPolicy e binding di pubblicazione stock sui canali
automation.configureAdminConfigurazione automazioni

Le mutazioni prive di una capability dedicata usano il controllo OMS write generale. Per esempio, i movimenti fisici di magazzino richiedono OMS write ma non inventory.configure, che protegge la configurazione outbound del canale. Gli Owner hanno il rango più alto. Gli endpoint pubblici del portale resi e i webhook inbound sono eccezioni ristrette: usano token o segreti della connessione, non una sessione utente.

Superficie API

Le route sono sotto /api nell'app OMS. La tabella raggruppa la superficie funzionale, senza elencare ogni parametro o variante dinamica.

AreaRoute principaliResponsabilità
Canali/channels, /channels/[id], /sync, /test, /inbound-webhook, /inventory-policy, /inventory-bindings, /connectionsConfigurazione, health, attivazione, sync e policy stock.
Webhook commerce/webhooks/commerce/[endpointId]Ingresso pubblico verificato per eventi del provider.
Ordini/orders, /orders/stats, /orders/export, /orders/[id], /confirm, /cancel, /ship, /label, /tracking/refresh, /send-supplierQuery, transizioni ordine e comandi provider.
Fulfillment/orders/[id]/fulfillment, /fulfillment/steps/[stepId]/scan, /completeRouting e avanzamento picking/packing.
Clienti/customers, /customers/[id], /customers/import/preview, /customers/import/[batchId]/commitAnagrafica e import controllato.
Inventario/warehouses, /inventory, /inventory/[id]/mutations, /inventory/transfers, /reservations, /stocktakesLedger, movimenti, trasferimenti, reservation e inventari.
Documenti magazzino/warehouse-documents, /warehouse-documents/[id]/pdfRegistrazioni operative PZ, WZ e MM.
Spedizioni/shipments, /shipments/export, /shipments/push-store, /shipments/[id], /label, /op, /recover-labelColli, etichette, tracking e recovery corriere.
Resi/returns, /returns/export, /returns/portal-settings, /returns/[id], /approve, /reject, /receive, /refund, /refund-resolutionWorkflow reso, stock e rimborso.
Portale resi/public/returns/lookup, /public/returns, /public/returns/[company]/[token]Lookup, apertura e consultazione token-scoped.
Automazioni/automations, /automations/[id], /dry-run, /events, /runsRegole revisionate, simulazione ed esecuzioni.
Operations/operations/blockers/[id], /operations/outbox/[id]/retry e route di risoluzione marketplace/corriereRetry ammessi, riconciliazione e chiusura blocker.

Le route validate restituiscono errori coerenti con il controllo applicato: 402 per modulo non incluso, 403 per accesso o capability insufficiente e 409 per conflitti di stato, fingerprint o concorrenza.

Idempotenza e concorrenza

L'idempotenza è definita per comando, non affidata a un retry generico:

  • la creazione canale richiede un header idempotency-key UUID e salva il fingerprint della richiesta; lo stesso payload restituisce la risorsa esistente, un payload diverso produce 409;
  • le mutazioni inventario richiedono una chiave nel body e applicano ledger e documento di magazzino nella stessa transazione; la risposta segnala se è un replay;
  • i rimborsi normalizzano il payload e ne confrontano il fingerprint; un replay equivalente non duplica l'effetto, uno incompatibile viene rifiutato;
  • le transizioni già raggiunte, come la conferma ordine, rispondono in modo idempotente;
  • le automazioni usano clientRequestId, revisione immutabile e lock token per evitare aggiornamenti persi.

Quando una route accetta una chiave, il client deve riutilizzare la stessa chiave soltanto per lo stesso comando logico. Riutilizzarla con un payload diverso è un conflitto, non una richiesta di overwrite.

Outbox e riconciliazione

Per gli effetti importanti, la transazione applicativa salva insieme stato dominio, evento e record outbox. Il worker reclama il record con token e lease, registra i tentativi e applica la tassonomia di retry del provider.

comando API
   -> transazione: aggregate + domain event + outbox
   -> wake-up worker
   -> claim con lease
   -> chiamata provider
      -> conferma: stato terminale
      -> errore retryable: nuovo tentativo pianificato
      -> esito ambiguo: UNKNOWN / blocker -> riconciliazione operativa

Un esito UNKNOWN non viene ritentato alla cieca: una seconda scrittura potrebbe duplicare spedizione, rimborso o aggiornamento marketplace. Le route operations consentono retry solo per eventi eleggibili e registrano le risoluzioni manuali.

OpenAPI e fonte di verità

Il comando pnpm gen:openapi carica oggi soltanto:

  • packages/web/lib/openapi/**/*.ts per lo spec web;
  • packages/worker/src/openapi/**/*.ts per lo spec interno worker.

Gli output sono packages/docs/public/openapi/web.json e packages/docs/public/openapi/worker.json. Fino all'estensione del generatore, il catalogo OMS in questa pagina è mantenuto manualmente e l'implementazione delle route sotto packages/oms/app/api/** resta la fonte di verità per payload e comportamento puntuale.

Limiti intenzionali e debito noto

  • Il catalogo prodotto appartiene al Feed Manager e non viene replicato come sottosistema OMS.
  • I documenti PZ/WZ/MM non hanno valore fiscale.
  • La disponibilità generale di un adapter richiede ancora test contrattuali con sandbox o credenziali live, anche quando il flusso applicativo è implementato.
  • La generazione OpenAPI OMS e alcune viste di dettaglio operative sono consolidamenti ancora aperti.
  • I casi multi-unità e le ricezioni parziali richiedono comandi e aggregate più granulari prima di essere estesi.

In questa pagina