@primocaredentgroup/magazzino
v0.5.0
Published
Componente Convex per gestione magazzino cliniche (ordini, inventario, fornitori).
Readme
@primocaredentgroup/magazzino
Componente Convex per ordini, inventario, fornitori, catalogo acquisti, configurazione delle stanze e controlli di magazzino delle cliniche.
Confini di responsabilità
Il componente possiede:
- schema e dati di magazzino;
- regole di dominio per ordini, inventario, catalogo e controlli;
- lifecycle dei materiali clinici, classificazioni prodotto, OCR/UDI normalizzato e record minimo del passaporto implantare;
- righe ordine normalizzate, dispatch vendor e ricezione DDT manuale;
- configurazione warehouse delle cliniche;
- audit tramite identificativi esterni dell'actor.
L'app host possiede:
- autenticazione, utenti, ruoli e permessi;
- aziende, gruppi, brand e anagrafica primaria delle cliniche;
- UI, routing e orchestrazione fra componenti.
Il componente non contiene tabelle utenti/ruoli e non assegna cliniche al primo
login. Ogni chiamata riceve dal server host un WarehouseActorContext corrente;
questo rende effettive immediatamente revoche e modifiche dello scope.
Installazione e mount
npm install @primocaredentgroup/magazzino convex// convex/convex.config.ts
import { defineApp } from "convex/server";
import magazzino from "@primocaredentgroup/magazzino/convex.config.js";
const app = defineApp();
app.use(magazzino);
export default app;API host tipizzata
exposeApi è il solo punto pubblico destinato al frontend. L'actor non compare
negli argomenti browser: l'host lo costruisce server-side e traduce le operazioni
astratte del componente nei propri permessi RBAC.
// convex/lib/magazzinoExposeApi.ts
import { exposeApi } from "@primocaredentgroup/magazzino";
import { components } from "../_generated/api";
import { buildWarehouseActorContext } from "./warehouseAccess";
export const magazzinoApi = exposeApi(components.magazzino, {
getActorContext: async (ctx, operation) => {
return await buildWarehouseActorContext(ctx, operation);
},
});Le operazioni stabili esposte al mapping host sono:
- configurazione:
warehouse.approval-rules.manage,warehouse.authorizations.manage,warehouse.categories.manage,warehouse.clinic-macro-scopes.manage,warehouse.clinics.managee la proiezione host-only tramite client tipizzato; - operatività:
warehouse.inventory.read,warehouse.inventory.write,warehouse.reporting.read,warehouse.news.read,warehouse.news.manage,warehouse.orders.read,warehouse.cart.read,warehouse.cart.write,warehouse.orders.writeewarehouse.orders.approve; - cataloghi:
warehouse.products.manage,warehouse.catalog-events.read,warehouse.vendor-catalog.manageewarehouse.vendors.manage; - workflow specializzati:
warehouse.feedback.write,warehouse.feedback.manage,warehouse.rooms-guide.manage,warehouse.safety-stock.manageewarehouse.tap-refill.manage. - materiali clinici:
warehouse.clinical-materials.read,warehouse.clinical-materials.writeewarehouse.clinical-materials.manage; - vendor/DDT:
warehouse.vendor-orders.read,warehouse.vendor-orders.dispatch,warehouse.vendor-orders.receiveewarehouse.vendor-orders.manage.
Il package esporta anche MagazzinoClient, ComponentApi e tutti i validator
pubblici tramite @primocaredentgroup/magazzino/validators. Non servono wrapper
generati, patch a node_modules o cast del component handle.
Scope dell'actor nella 0.5
WarehouseActorContext distingue due concetti che l'host deve derivare
server-side:
clinicExternalIdselenca le cliniche sulle quali l'actor può operare;clinicScopevaleassigned_clinicsoppureall_clinicse dichiara se un amministratore può modificare configurazioni globali condivise da tutte le cliniche.
clinicScope: "all_clinics" non sostituisce l'elenco
clinicExternalIds: le normali letture e scritture clinica-specifiche restano
sempre verificate contro quell'elenco. L'accesso system è riservato alle
orchestrazioni host fidate.
Nella 0.5 richiedono un amministratore con scope all_clinics:
- modifica delle policy temporali e delle regole di eleggibilità clinica;
- classificazione clinica dei prodotti;
- lettura e modifica della configurazione globale dei provider vendor.
Il rollout deve quindi essere coordinato: prima di usare queste scritture con
la 0.5, l'host deve popolare clinicScope nel proprio hook
getActorContext. Un amministratore senza questo campo viene trattato in modo
prudente come non globale e la scrittura viene rifiutata.
Proiezione delle cliniche
La chiave di integrazione è clinicExternalId: string. In PrimoUpCore deve
corrispondere al codice clinica stabile, preservando anche zeri iniziali e codici
non numerici. Non usare _id Convex del Core e non convertire il codice in numero.
L'host sincronizza soltanto la proiezione organizzativa mediante
clinics.upsertProjection. L'upsert aggiorna nome, area, azienda, gruppo, brand,
timezone e stato senza sovrascrivere stanze, riuniti e budget warehouse.
Sviluppo e verifica
npm test
npm run typecheck
npm run verify
npm pack --dry-runnpm run codegen richiede un deployment Convex configurato ed è intenzionale,
ma non fa parte di prepublishOnly: il contratto ComponentApi del package è
derivato dal codice sorgente locale e non da un deployment host potenzialmente
obsoleto.
Gli helper di @primocaredentgroup/magazzino/test costruiscono un actor fidato
senza persistere utenti nel componente.
Materiali clinici e vendor nella 0.5
Il client tipizzato espone due namespace aggiuntivi:
await client.clinicalMaterials.ensureDefaultWindowPolicies(ctx, { actor });
await client.clinicalMaterials.syncCase(ctx, {
actor,
caseCode: "core:clinical-case:...",
clinicExternalId: "001",
domain: "implant",
patientCode: "core:patient:...",
appointmentCode: "core:appointment:...",
carePlanRowCode: "core:care-plan-row:...",
appointmentDate: "2026-08-18",
evaluationDate: "2026-08-13",
sourceFacts: [{ kind: "service_code", code: "IMPLANT" }],
});
await client.vendorOrders.requestDispatch(ctx, {
actor,
orderId,
provider: "astidental",
idempotencyKey: "host-request-id",
});Le policy delle finestre sono configurazioni versionate e inizializzabili in
modo idempotente con ensureDefaultWindowPolicies. Gli identificativi host sono
codici opachi; in particolare list_row_external_id è un ripiego transitorio
finché Listini non espone una chiave business durevole.
Il gate host deve usare clinicalMaterials.countRequiredOpenCases, che valuta
senza il limite di paginazione della workspace tutti i casi aperti dei domini
richiesti. listCases resta invece una query di presentazione con limite massimo
di 200 elementi.
orders.items e lo stato italiano restano leggibili nella fase di widen. I
nuovi flussi vendor usano orderLines e stati distinti per approvazione,
dispatch e ricezione.
Gli adapter supportati in questa release sono intenzionalmente fake o
manual: non eseguono traffico reale. OCR reale, autenticazione Straumann,
credenziali e contratti Astidental/Straumann devono essere forniti dall'host al
momento dell'attivazione e non vengono memorizzati nelle tabelle del componente.
Migrazione alla 0.5
La 0.5 aggiunge soltanto tabelle e campi opzionali allo schema 0.4. Il vecchio
array orders.items resta disponibile durante il dual-read e viene trasformato
idempotentemente in orderLines. Nessun reset o backfill distruttivo è incluso.
Le modifiche a policy temporali o regole di eleggibilità non riscrivono lo storico in autonomia: i casi aperti conservano la versione applicata e devono essere rivalutati dall'orchestrazione host. Prima di attivare un gate su dati già esistenti, l'host deve inoltre eseguire il proprio bootstrap degli appuntamenti nel perimetro concordato.
Migrazione alla 0.4
La 0.4 è un cambio di contratto:
- rimuove API e tabelle component-owned per utenti, ruoli e permessi;
- sostituisce gli ID clinica numerici con
clinicExternalIdstringa; - richiede l'hook host
getActorContext; - deriva nomi e audit dal contesto fidato o dai dati di dominio;
- espone argomenti e ritorni completamente tipizzati.
Come previsto dal piano di integrazione 2026, gli ambienti correnti non contengono dati reali di magazzino: la 0.4 parte da uno stato componente nuovo. Non è previsto un backfill automatico dei vecchi dati demo.
La pubblicazione npm resta manuale perché richiede il 2FA dell'owner.
Licenza
Proprietario — Primo Care Dent Group. Uso interno.
