Contatori sequenziali senza buchi
Assegna una sequenza leggibile — numeri di fattura, codici ticket, ID di protocollo — che resti contigua. Alloca i numeri tu, non dall'id auto-generato del record.
Preferisci l'azione nativa di sequenza su un campo Number univoco. Bobuild mantiene un contatore interno per chiave, lo blocca nella stessa transazione del salvataggio e non riusa un numero dopo un rollback o dopo che elimini una riga di business.
Perché non usare {id}?
Ogni record riceve un id sequenziale assegnato automaticamente. Va bene per i collegamenti interni e per codici come P{id}, ma la sequenza può lasciare buchi: create annullate, submit falliti dopo che l'id era già riservato, bozze eliminate e casi simili saltano dei numeri. Quando regole fiscali o operative richiedono nessun buco, alloca un number di business separato.
Quando usarlo
- Numeri di fattura / ricevuta che devono essere contigui
- Numeri di protocollo o pratica senza valori saltati
- Numeri che ripartono ogni anno (o per filiale, tipo documento, …)
- Qualsiasi contatore pubblico in cui gli auditor si aspettano
n,n+1,n+2senza mancanze dentro ciascuna sequenza
Se i buchi sono accettabili, un auto field testuale come INV-{id:lpad(5, '0')} è più semplice.
Sequenza nativa (consigliata)
Modello dati
| Oggetto | Campi chiave |
|---|---|
| Invoice (esempio) | number (Number, Unique da solo), più i tuoi campi di business |
Oppure, per ripartire ogni anno (o altra sezione):
| Oggetto | Campi chiave |
|---|---|
| Invoice | year (Text o Number), number (Number) — entrambi Unique insieme |
Il gruppo di unicità è la chiave della sequenza:
numberunique da solo → una sequenza per tutto l'oggettonumberunique conyear(obranch, …) → una sequenza per valore partner
Vedi Unicità. Non creare un oggetto Counter separato per le app nuove.
Passaggi
- Su Invoice, aggiungi il campo Number
numbere abilita Unique (da solo, o insieme ayear/ altri campi chiave). - Tieni
number(e i partner comeyear) in sola lettura sul form di creazione. - Opzionalmente deriva un'etichetta pubblica con un auto field testuale, ad esempio
{year}/{number}oINV-{year}-{number:lpad(5, '0')}. - Sul form di creazione (o sul task di create API), aggiungi un'azione Aggiorna questo record (o includi i campi in un Crea) che imposta:
- ogni partner di chiave (
year, …) — costante, formula o copia dal form; number→ al prossimo valore di sequenza.
- ogni partner di chiave (
I campi partner devono essere impostati nella stessa azione (o già presenti sulla riga in caso di update). I task del form corrono nella stessa transazione del salvataggio: le create concorrenti si serializzano sul contatore interno corrispondente e i rollback non consumano numeri.
Il primo valore emesso è 1. Chiavi di unicità diverse (ad esempio 2026 vs 2027) hanno sequenze indipendenti.
Scelta della chiave
| Esigenza | Gruppo di unicità |
|---|---|
| Una sequenza che non riparte mai | number unique da solo |
| Riparte ogni anno solare | Unique su number + year (FORMAT(invoice_date, 'Y') o FORMAT(NOW(), 'Y')) |
| Anno fiscale (es. apr–mar) | Unique su number + campo anno fiscale |
| Per filiale / sede | Unique su number + branch (o branch + year) |
| Più serie (fatture vs note di credito) | Unique su number + Text key come CONCAT(..., '-INV') |
Contatori manuali (legacy)
Prima dell'azione nativa, lo stesso comportamento si costruiva con un oggetto Counter (o YearlyCounter) esplicito e due passi di task: incrementa il contatore, poi timbra la fattura. Continua a funzionare se lo usi già; per le app nuove preferisci la sequenza nativa sopra.
Non serve una Reference da Invoice al contatore, e non cerchi il contatore con un filtro di Update per leggerlo. Dopo un Create (anche quando l'unicità riusa una riga esistente), le azioni successive possono copiare i campi da the last created Counter/YearlyCounter — vedi Create.
Una sequenza (contatore globale)
| Oggetto | Campi chiave |
|---|---|
| Counter (global) | number (Number) — ultimo valore emesso |
| Invoice | number (Number), più campi di business |
- Crea Counter globale con Number
number(default0); opzionalmente imposta il valore iniziale. - In creazione, Aggiorna il Counter e imposta
numbera{number}+1. - Aggiorna questo record e imposta Invoice
numberalnumberdel Counter (i globali sono disponibili come sorgente di valore; c'è una sola riga).
Sequenze per anno (YearlyCounter)
| Oggetto | Campi chiave |
|---|---|
| YearlyCounter (non globale) | year (Unique), number |
| Invoice | year, number (unique insieme) |
Sul form di creazione, esegui queste azioni in ordine:
-
Crea un YearlyCounter:
year← la chiave (ad esempioFORMAT(invoice_date, 'Y')oFORMAT(NOW(), 'Y'));number←0.
Poiché
yearè unique, la prima fattura di quell'anno inserisce la riga; le successive la riusano. In entrambi i casi Bobuild registra quella riga come the last created YearlyCounter per il resto del task. In caso di riuso, Create non resettanumbera0— la mappa campi del Create si applica solo su un insert reale. -
Aggiorna quel YearlyCounter e imposta
numbera{number}+1. Selezionalo con un filtro suyear(stessa formula di sopra), oppure suiduguale all'id dell'ultimo YearlyCounter creato. -
Aggiorna questo record (la Invoice):
- imposta
yearalla stessa chiave; - imposta
numberalnumberdi the last created YearlyCounter (il valore appena scritto).
- imposta
Per il passo di timbratura non servono un campo Reference né una regola auto — nel selettore di valore scegli i campi da the last created YearlyCounter.
Consigli
- Preferisci al prossimo valore di sequenza su un Number unique piuttosto che mantenere un oggetto Counter visibile.
- Non riusare l'auto field
{id}per la stessa sequenza pubblica — tieni separatiidinterno enumberdi business. - Eliminare una riga di business non libera il suo numero per il riuso (comportamento adatto al fiscale).
- Gli stessi pattern funzionano sugli endpoint API Create quando i record nascono fuori dall'UI.
Correlati
- Unicità — chiave di sequenza = gruppo di unicità
- Operazioni sui record — create, update, azioni sui campi
- Task dei form
- Oggetti globali — solo per il pattern Counter legacy
- Auto fields — quando bastano codici basati su
{id} - Placeholder e funzioni —
FORMAT,NOW,CONCAT