Composables

useSpreadsheetERP

Catalogo sorgenti ERP, gestione sorgenti attive sul foglio, refresh debounced sui filtri globali, auto-refresh interval.

Signature

function useSpreadsheetERP(options: UseSpreadsheetERPOptions): UseSpreadsheetERPReturn

Gestisce catalogo sorgenti ERP, sorgenti attive sul foglio, fetch in parallelo e refresh debounced quando cambiano i filtri globali.

Opzioni

interface UseSpreadsheetERPOptions {
  idfolio: Ref<number | null>
  filtriGlobali: Ref<GlobalFilters>
  sorgentiConfig: Ref<DataSourceConfig[]>
  refreshInterval?: Ref<number | null>      // secondi
}
OpzioneDescrizione
idfolioID foglio corrente (per logging/contesto)
filtriGlobaliFiltri globali reattivi del foglio. Cambio → debouncedRefreshAll (500ms)
sorgentiConfigSorgenti dichiarate sul foglio (tipicamente computed da state.sorgentierp)
refreshIntervalSe valorizzato > 0, setInterval(refreshAll, n * 1000)

Risultato

interface UseSpreadsheetERPReturn {
  catalogo: Ref<SorgenteCatalogo[]>
  sorgentiAttive: Ref<ErpDataSource[]>
  isRefreshing: Ref<boolean>
  lastRefreshAt: Ref<Date | null>
  errors: Ref<Record<string, string>>          // chiave: codice sorgente
  isCatalogoLoading: Ref<boolean>

  caricaCatalogo(): Promise<void>
  addSource(config: DataSourceConfig): void
  removeSource(codice: string): void
  refreshAll(): Promise<void>
  refreshSource(codice: string): Promise<void>
  aggiornaFiltriSorgente(codice: string, filtri: Record<string, unknown>): void
}

ErpDataSource

interface ErpDataSource {
  codice: string
  config: DataSourceConfig
  righe: ErpRow[]                              // dati caricati
  totaleRighe: number                          // da Content-Range
  lastFetchAt: Date | null
  isLoading: boolean
  error: string | null
}

Comportamento

Carica il catalogo da FogliPostgrestService.catalogo() la prima volta. Idempotente: se catalogo.value.length > 0, ritorna immediatamente.

addSource(config)

Aggiunge una sorgente alla lista attiva. Idempotente: se il codice esiste già, no-op.

erp.addSource({
  codice: 'fatture_riepilogo',
  cellaDestinazione: 'A1',
  sheetIndex: 0,
  filtri: { tipooperazione: 'V' },
  usaFiltriGlobali: true,
  limitRighe: 500,
})
addSourcenon triggera un fetch automatico. Devi chiamare refreshSource(codice) o refreshAll() esplicitamente. (SpreadsheetEditor lo fa al mount.)

removeSource(codice)

Rimuove la sorgente dalla lista attiva e pulisce eventuali errori associati.

refreshSource(codice)

Fetch di una sola sorgente:

1. Cerca la sorgente in sorgentiAttive
2. Cerca l'entry nel catalogo (per endpoint)
3. isLoading = true, error = null
4. ErpDataService.fetchSorgente(config, filtriGlobali, catalogoEntry)
5. Aggiorna righe, totaleRighe, lastFetchAt
6. isLoading = false

In caso di errore: errors[codice] viene popolato e source.error aggiornato.

refreshAll()

Promise.allSettled su tutte le sorgenti attive. Aggiorna lastRefreshAt e isRefreshing (globale).

isRefreshing.value = true
await Promise.allSettled(codici.map((c) => refreshSource(c)))
lastRefreshAt.value = new Date()
isRefreshing.value = false

Le sorgenti che falliscono non bloccano le altre.

Watcher reattivo su filtriGlobali

const debouncedRefreshAll = debounce(refreshAll, 500)
watch(filtriGlobali, () => {
  if (sorgentiAttive.value.length > 0) debouncedRefreshAll()
}, { deep: true })

Tutte le sorgenti con config.usaFiltriGlobali === true riceveranno i nuovi filtri (composti con i filtri specifici di ciascuna).

aggiornaFiltriSorgente(codice, filtri)

Modifica i filtri specifici di una sorgente attiva (sovrascrive config.filtri). Non triggera fetch automatico — fallo manualmente dopo:

erp.aggiornaFiltriSorgente('fatture_riepilogo', { tipooperazione: 'A' })
await erp.refreshSource('fatture_riepilogo')

Auto-refresh interval

watch(refreshInterval, () => {
  stopAutoRefresh()
  avviaAutoRefresh()
}, { immediate: true })

function avviaAutoRefresh() {
  const interval = refreshInterval?.value
  if (!interval || interval <= 0) return
  autoRefreshInterval = setInterval(() => refreshAll(), interval * 1000)
}

Cleanup su onUnmounted.

Esempio: dashboard ERP read-only

<script setup lang="ts">
import { ref, computed } from 'vue'
import { useSpreadsheetERP } from '@pzeta/vue-foglidicalcolo'

const idfolio = ref<number | null>(42)

const filtriGlobali = ref({
  periodo: { da: '2026-01-01', a: '2026-12-31' },
})

const sorgentiConfig = ref([
  {
    codice: 'fatture_riepilogo',
    cellaDestinazione: 'A1',
    sheetIndex: 0,
    filtri: { tipooperazione: 'V' },
    usaFiltriGlobali: true,
  },
])

const erp = useSpreadsheetERP({
  idfolio,
  filtriGlobali,
  sorgentiConfig,
  refreshInterval: ref(60),     // refresh ogni 60s
})

await erp.caricaCatalogo()
sorgentiConfig.value.forEach((c) => erp.addSource(c))
await erp.refreshAll()
</script>

<template>
  <div v-for="s in erp.sorgentiAttive.value" :key="s.codice">
    <h3>{{ s.codice }} ({{ s.totaleRighe }} righe)</h3>
    <p v-if="s.isLoading">Caricamento...</p>
    <p v-else-if="s.error" class="text-red-600">⚠ {{ s.error }}</p>
    <p v-else>Ultimo aggiornamento: {{ s.lastFetchAt }}</p>
  </div>
</template>

Tipi correlati