Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,12 @@ La nota *"se i numeri sono questi cosa devo fare per crescere?"* suggerisce un l
| **2** | Schema DB completo del motore finanziario (piano dei conti, tag, saldi, periodi) | Schema applicato, migrazioni versionate | ✅ **Fatta** (sessione 1) |
| **3** | Motore di riclassificazione + indici (da `docs/MODELLO_FINANZIARIO.md`), **import Excel §11.1** | Import di un vero file cliente → Conto Economico riclassificato corretto | 🟡 **Quasi**: motore, indici e import fatti e verificati. Manca il file di un cliente **con i saldi**: quello consegnato è un modello vuoto (vedi `docs/MODELLO_FINANZIARIO.md` §8-bis) |
| **4** | UI Business: Panoramica + Conto Economico + Stato Patrimoniale (§10.2-10.4) | Le 3 schermate con dati reali importati | ✅ **Fatta** (sessione 1) — menu laterale e grafici compresi |
| **5** | UI Capitale Circolante + Tesoreria/Cash Flow + Scadenziario (§10.5-10.6) | Previsione di cassa funzionante su dati reali | ⬜ Prossima |
| **6** | UI Banche e Finanziamenti (§10.7) + collegamento rate→Cash Flow | Fidi/finanziamenti con impatto visibile in Tesoreria | ⬜ |
| **5** | UI Capitale Circolante + Tesoreria/Cash Flow + Scadenziario (§10.5-10.6) | Previsione di cassa funzionante su dati reali | ✅ **Fatta** (sessione 2) |
| **6** | UI Banche e Finanziamenti (§10.7) + collegamento rate→Cash Flow | Fidi/finanziamenti con impatto visibile in Tesoreria | ⬜ Prossima |
| **7** | Analisi & Simulazioni (§10.8) | Scenario what-if salvabile e confrontabile | ⬜ |
| **8** | Sync Consulente↔Azienda via Tailscale (§6) + status bar (§7) | Due installazioni reali che si scambiano dati | ⬜ |
| **9** | Import Excel avanzato: tolleranza a varianti di formato tra clienti/periodi (§11.1) | Import robusto su più file Excel reali diversi tra loro | ⬜ |
| **10** | Installer offline (electron-builder) per Consulente e Azienda | `.exe` funzionanti, Tailscale bundled | 🟡 **Parziale**: `.exe` installabile e portable funzionanti (v0.0.1). Mancano le due varianti separate e Tailscale bundled, che hanno senso solo dopo la Fase 8 |
| **10** | Installer offline (electron-builder) per Consulente e Azienda | `.exe` funzionanti, Tailscale bundled | 🟡 **Parziale**: `.exe` installabile, portable e demo funzionanti. Mancano le due varianti separate e Tailscale bundled, che hanno senso solo dopo la Fase 8. **Decisione del cliente (2026-09-16): nessuna release fino a codice finito; alla fine una sola release con tre eseguibili — installer, portable, demo** |
| **11+** | Integrazioni Fase futura: connettore IRIS, Cassetto Fiscale, Open Banking, pianificazione fiscale, marginalità multi-dimensionale, assistente numeri | Una alla volta, dopo validazione col cliente | ⬜ |

### 13-bis. Stato alla fine della sessione 1 (Fasi 0 → 4)
Expand Down Expand Up @@ -424,7 +424,53 @@ Tre garanzie, perché una demo con credenziali note non deve mai confondersi con

⚠️ **Resta aperto, da chiarire col consulente**: ROE e ROI confrontano un flusso (utile, EBIT) con uno stock (patrimonio, capitale investito). §5 non dice di annualizzarli, quindi su un mese valgono circa un dodicesimo del valore annuale. Il bilancio annuale li mostra corretti.

**Da fare in Fase 5**: Capitale Circolante e Tesoreria (§10.5-§10.6). Gli indici del circolante sono già calcolati dal motore; la previsione di cassa richiede scadenziario e previsioni manuali, che sono dati nuovi.
### 13-ter. Sessione 2 — modello Excel e Fase 5

**Modello Excel scaricabile.** Provando la versione portatile, un'azienda appena creata restava bloccata: l'unica porta d'ingresso dei dati è l'import, che vuole il file del consulente. Ora *Import dati* (e la schermata vuota di un'azienda senza bilancio) offre **Scarica il modello Excel**: stesse intestazioni e sezioni che l'import riconosce, TIPO a tendina, sotto-classificazioni a "X" solo dove la sezione le prevede, un foglio ISTRUZIONI, e i conti già presenti dell'azienda. Un test genera il modello dalle 24 sezioni della migrazione 002, lo compila e lo reimporta senza perdite (`src/main/import/template.test.ts`).

**Fase 5 — Capitale Circolante (§10.5)**

| Pezzo | Dove |
|---|---|
| Componenti, variazioni, media mobile, note automatiche | `src/shared/engine/working-capital.ts` |
| Confronti e storia a 24 mesi | `src/main/server/services/working-capital.service.ts` |
| Schermata | `src/renderer/src/pages/business/WorkingCapitalView.tsx` |

Le card confrontano con **la fine dell'anno precedente** (dicembre, o il bilancio annuale se dicembre manca), la tabella degli indici con **lo stesso periodo dell'anno prima**: sono i due confronti dei mockup. La media mobile del CCC è a **3 mesi** e si calcola solo su mesi consecutivi.

Le **note automatiche** sono regole, come chiede §6 del modello, con soglie che il modello non fissa (§9): un indice di ciclo si segnala da **5 giorni** di differenza, una componente da **±10%**, i crediti scaduti da oltre 60 giorni quando pesano almeno il **10%** dei crediti aperti. Sono in `SOGLIE_NOTE`, un punto solo. ⚠️ Da validare col consulente. Un aumento di DPO è una nota **neutra**: pagare più tardi aiuta la cassa ma può essere un segnale di tensione.

**Fase 5 — Tesoreria / Cash Flow e Scadenziario (§10.6)**

Migrazione `004_treasury`:

| Tabella | Cosa tiene |
|---|---|
| `treasury_items` | Ogni movimento atteso. `source` distingue le fatture dello **scadenziario**, le **previsioni manuali** (anche mensili ricorrenti) e — dalla Fase 6 — le rate dei **finanziamenti**. Documento, condizioni e modalità di pagamento dal DICTIONARY (§8 del modello). Incassi parziali in `paid_cents`: lo stato "Pagata / Non pagata / Pagamento parziale" si ricava, non si memorizza |
| `company_treasury_settings` | Soglia minima di liquidità e ultimo saldo di banca noto |

`docs/MODELLO_FINANZIARIO.md` **non definisce la previsione di cassa**: le regole di `src/shared/engine/treasury.ts` sono scelte di implementazione, dichiarate nel codice e da validare col consulente.

1. **La previsione è una somma di movimenti datati**, nessuna proiezione statistica: ogni euro si ritrova in una riga.
2. **Liquidità di partenza** = il più recente fra il saldo inserito a mano e le liquidità immediate dell'ultimo bilancio a consuntivo, più incassi e pagamenti registrati dopo quella data.
3. **Le scadenze passate e non saldate entrano oggi**, marcate come scadute: un credito scaduto è ancora un incasso atteso. **Le previsioni manuali passate invece si scartano**: erano stime, il consuntivo le ha superate.
4. **Condizioni di pagamento** (RD, DF, FM — il file non le spiega): RD e DF = data documento + giorni; FM = data documento + giorni, poi a fine mese. È la lettura d'uso comune.
5. **Orizzonti** 7, 30, 60, 90 giorni e 6 mesi, cumulati da oggi. La **tensione finanziaria** è il primo giorno in cui la liquidità prevista scende sotto la soglia minima (sotto zero, se la soglia non c'è), cercato giorno per giorno: un campionamento settimanale potrebbe saltarlo. È anche l'avviso "Tensione finanziaria tra N giorni" della Panoramica (§10.2), insieme a quello sui pagamenti scaduti.

La **Panoramica** ora mostra liquidità di oggi, cash flow a 30 giorni e il grafico con storico e previsione, soglia minima e marcatore OGGI. "Affidamenti disponibili" resta un trattino fino alla Fase 6.

**Permessi**: l'operatore Azienda vede tesoreria e scadenziario ma non li modifica. È la scelta prudente finché resta aperto §14 punto 4 (autonomia dell'app Azienda), che cambia il modello di sync.

**Dati della demo**: scadenziario e previsioni della Pizzeria DaProd hanno **date relative al giorno del primo avvio** (`src/main/db/demo-treasury.ts`): una previsione guarda avanti da oggi, e una demo aperta fra sei mesi con tutte le scadenze nel passato mostrerebbe solo arretrati. Ci sono di proposito un credito scaduto da oltre 60 giorni, un incasso parziale e un pagamento scaduto. Un database demo creato da una versione precedente riceve la tesoreria al primo avvio, una volta sola. La **rata del mutuo** è per ora una previsione manuale: col modulo Banche diventerà un finanziamento vero.

**Due correzioni emerse lungo la strada**

- **Serie storica**: mescolava mesi e bilancio annuale sullo stesso asse (un punto annuale in mezzo ai mesi vale dodici volte gli altri), e applicava il limite di 24 punti prima di scartare i periodi senza saldi, così i mesi di solo budget rubavano posto alla storia.
- **Palette**: alcune tonalità usate dall'interfaccia (`ink-500`, `ink-200`, `brand-200`) non erano definite, e i testi secondari uscivano bianchi.

**Verifiche**: 54 test (`npm run test`), fra cui la previsione di cassa calcolata a mano giorno per giorno; `npm run verify:schema` passa da 22 a 35 controlli con i vincoli della 004; percorso completo provato nell'app: incasso parziale che aggiorna liquidità e previsione, fattura "60 giorni fine mese" con scadenza calcolata, aggiornamento di un database demo della 0.0.4.

**Da fare in Fase 6**: Banche e Finanziamenti (§10.7), con le rate che entrano nella previsione come `source = 'finanziamento'`, gli affidamenti disponibili nella Tesoreria e il DSCR finalmente calcolabile.

---

Expand Down
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Un gestionale desktop per consulenti finanziari aziendali: riclassifica il bilan
calcola gli indici che contano, previene le tensioni di cassa prima che arrivino,
e risponde a "cosa succede se" senza toccare un foglio Excel.

![stato](https://img.shields.io/badge/stato-in%20sviluppo%20%C2%B7%20fase%204%2F10-3ddbff)
![stato](https://img.shields.io/badge/stato-in%20sviluppo%20%C2%B7%20fase%205%2F10-3ddbff)
![piattaforma](https://img.shields.io/badge/Windows-x64-3ddbff)
[![licenza](https://img.shields.io/badge/licenza-MIT-5cff9d)](LICENSE)

Expand Down Expand Up @@ -140,15 +140,33 @@ nessun dominio da comprare, nessun dato che passa da terzi per l'uso quotidiano.
- **Banche e Finanziamenti** — fidi, mutui e leasing, e quanto pesano sulla cassa futura
- **Analisi & Simulazioni** — "cosa succede se": assumo, investo, alzo i prezzi

## Come entrano i dati

Due strade, che si usano insieme.

**Il bilancio, da Excel.** Dentro un'azienda, alla voce *Import dati*, c'è il pulsante
**Scarica il modello Excel**: un file con tutte le sezioni già pronte, e i conti
dell'azienda se ce ne sono già. Si scrive il saldo di ogni conto, si ricarica il file,
si sceglie a quale mese e a quale scenario appartiene (consuntivo, budget, forecast).
Prima di scrivere qualsiasi cosa, il programma mostra un riepilogo di quello che ha
letto. Il consulente che ha già il suo file può caricare direttamente quello.

**Le scadenze e le previsioni, a mano.** Nella *Tesoreria* si inseriscono le fatture da
incassare e da pagare, con le condizioni di pagamento (*30 giorni fine mese* e simili:
la scadenza si calcola da sola), e le voci che si ripetono — stipendi, affitto, incassi
di cassa. Quando una fattura viene pagata, anche solo in parte, lo si registra con un
clic, e la liquidità di oggi si aggiorna.

---

## A che punto siamo

Il programma si avvia, riconosce chi entra, gestisce l'anagrafica dei clienti e delle
loro aziende, importa il piano dei conti da Excel e ne calcola il bilancio
riclassificato con tutti gli indici — e adesso lo **mostra**: panoramica con avvisi
automatici, conto economico riclassificato e stato patrimoniale con tutti gli indici.
Quello che manca è la parte di cassa: previsione, scadenziario, banche, simulazioni.
riclassificato con tutti gli indici, e lo mostra in cinque schermate: panoramica con
avvisi automatici, conto economico, stato patrimoniale, capitale circolante con le sue
note automatiche, e tesoreria con scadenziario e previsione di cassa a sei mesi.
Mancano banche e finanziamenti, le simulazioni e il collegamento fra i due programmi.

| | Fase | Stato |
|---|---|---|
Expand All @@ -157,9 +175,11 @@ Quello che manca è la parte di cassa: previsione, scadenziario, banche, simulaz
| 2 | Struttura dati del motore di calcolo | ✅ Fatta |
| 3 | Riclassificazione, indici, import Excel | 🟡 Quasi — serve un file cliente **con i saldi** per la verifica finale |
| 4 | Le prime tre schermate di analisi | ✅ Fatta |
| 5-7 | Cassa, banche, simulazioni | ⬜ Prossima |
| 5 | Capitale circolante, tesoreria, scadenziario | ✅ Fatta |
| 6 | Banche e finanziamenti | ⬜ Prossima |
| 7 | Analisi e simulazioni | ⬜ |
| 8-9 | Collegamento fra i due programmi | ⬜ |
| 10 | Installatori | 🟡 Già disponibili, da rifinire |
| 10 | I tre eseguibili finali: installer, portable, demo | 🟡 Già provati, si pubblicano a codice finito |

La roadmap completa, con il dettaglio di cosa c'è dentro ogni fase, è in
[`AGENTS.md` §13](./AGENTS.md).
Expand All @@ -168,7 +188,8 @@ La roadmap completa, con il dettaglio di cosa c'è dentro ogni fase, è in

Il modo più rapido per vederlo all'opera è la **versione demo**:
`DaProdFinanza-Demo-x.y.z-portable.exe`. Si lancia senza installare niente e parte
già con una pizzeria di esempio e due anni di bilanci caricati. Si entra come
già con una pizzeria di esempio, due anni di bilanci, lo scadenziario e le previsioni
di cassa. Si entra come
consulente con **`cammo` / `1234`**, oppure come azienda con **`Pizzeria DaProd` /
`1234`**. I suoi dati restano in una cartella a parte e non si mescolano mai con
quelli di un'installazione vera.
Expand Down
3 changes: 2 additions & 1 deletion docs/MODELLO_FINANZIARIO.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,5 +291,6 @@ Nel foglio `PIANO DEI CONTI` ogni sezione dichiara a quali aggregati contribuisc

- Aliquote fiscali per forma giuridica/regione (per la pianificazione fiscale, `AGENTS.md` §11.6).
- Soglie di alert per la Panoramica (es. a che punto "Margine operativo -22%" diventa un alert rosso) — negli screenshot i valori sono plausibili ma le soglie esatte non sono documentate nel file Excel.
- Schema esatto delle "note automatiche" nel tab Capitale Circolante (§6) — dedotto dagli screenshot, non specificato nel file.
- Schema esatto delle "note automatiche" nel tab Capitale Circolante (§6) — dedotto dagli screenshot, non specificato nel file. *Implementate con soglie ipotizzate (5 giorni sugli indici di ciclo, ±10% sulle componenti, 10% di crediti scaduti oltre 60 giorni): vedi `AGENTS.md` §13-ter.*
- Regole della previsione di cassa (tab Tesoreria): il file non la tratta. L'app somma movimenti datati — scadenze aperte, previsioni manuali, rate — partendo dall'ultimo saldo noto; le scadenze passate e aperte entrano oggi. Anche il significato delle condizioni di pagamento RD/DF/FM (§8) è una lettura d'uso comune da confermare. Dettaglio in `AGENTS.md` §13-ter.
- Formato del file XML di fatture elettroniche da importare (vedi `AGENTS.md` §11.2 e §14).
62 changes: 62 additions & 0 deletions scripts/verify-schema.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,68 @@ app.whenReady().then(() => {
check('stesso conto, periodo e scenario', () => insertBalance(randomUUID(), accountUuid, monthUuid, 'actual', 999), 'rifiutato')
check('scenario inventato', () => insertBalance(randomUUID(), accountUuid, yearUuid, 'consuntivo', 100), 'rifiutato')
check('saldo su un conto inesistente', () => insertBalance(randomUUID(), randomUUID(), monthUuid, 'actual', 100), 'rifiutato')

const insertItem = (fields) => {
const row = {
direction: 'in',
source: 'scadenziario',
due_date: `${ANNO}-01-31`,
amount_cents: 10000,
paid_cents: 0,
paid_date: null,
recurrence: 'none',
payment_terms: null,
...fields
}
db.prepare(
`INSERT INTO treasury_items (uuid, company_uuid, direction, source, category, description,
payment_terms, due_date, amount_cents, paid_cents, paid_date,
recurrence, created_at, updated_at, synced, deleted)
VALUES (?, ?, ?, ?, 'Clienti', 'prova', ?, ?, ?, ?, ?, ?, ?, ?, 0, 0)`
).run(
randomUUID(), company.uuid, row.direction, row.source, row.payment_terms, row.due_date,
row.amount_cents, row.paid_cents, row.paid_date, row.recurrence, now, now
)
}

console.log('\n Tesoreria e scadenziario')
check('fattura da incassare', () => insertItem({}), 'accettato')
check('incasso parziale con data', () => insertItem({ paid_cents: 4000, paid_date: `${ANNO}-02-01` }), 'accettato')
check('previsione manuale ricorrente', () => insertItem({ source: 'manuale', direction: 'out', recurrence: 'monthly' }), 'accettato')
check('importo zero', () => insertItem({ amount_cents: 0 }), 'rifiutato')
check('incassato più del totale', () => insertItem({ paid_cents: 20000, paid_date: `${ANNO}-02-01` }), 'rifiutato')
check('data di incasso senza importo incassato', () => insertItem({ paid_date: `${ANNO}-02-01` }), 'rifiutato')
check('fattura dello scadenziario ricorrente', () => insertItem({ recurrence: 'monthly' }), 'rifiutato')
check('verso inventato', () => insertItem({ direction: 'entrata' }), 'rifiutato')
check('condizione di pagamento fuori dizionario', () => insertItem({ payment_terms: 'XX' }), 'rifiutato')

// Un'azienda temporanea, perché quella esistente può avere già le sue
// impostazioni e l'indice univoco le proteggerebbe.
const clientUuid = randomUUID()
const tempCompany = randomUUID()
db.prepare(
`INSERT INTO clients (uuid, code, name, archived, created_at, updated_at, synced, deleted)
VALUES (?, ?, 'Cliente di prova', 0, ?, ?, 0, 0)`
).run(clientUuid, codice('CLI'), now, now)
db.prepare(
`INSERT INTO companies (uuid, client_uuid, code, name, archived, created_at, updated_at, synced, deleted)
VALUES (?, ?, ?, 'Azienda di prova', 0, ?, ?, 0, 0)`
).run(tempCompany, clientUuid, codice('AZ'), now, now)

const insertSettings = (cash, date, min = 100) =>
db
.prepare(
`INSERT INTO company_treasury_settings (uuid, company_uuid, min_liquidity_cents,
opening_cash_cents, opening_cash_date,
created_at, updated_at, synced, deleted)
VALUES (?, ?, ?, ?, ?, ?, ?, 0, 0)`
)
.run(randomUUID(), tempCompany, min, cash, date, now, now)

check('saldo di cassa senza data', () => insertSettings(5000, null), 'rifiutato')
check('soglia minima negativa', () => insertSettings(null, null, -1), 'rifiutato')
check('impostazioni valide', () => insertSettings(5000, `${ANNO}-01-01`), 'accettato')
check('seconde impostazioni per la stessa azienda', () => insertSettings(null, null), 'rifiutato')
} finally {
db.exec('ROLLBACK')
db.close()
Expand Down
9 changes: 7 additions & 2 deletions src/main/db/demo-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,18 @@ function conImposte(valori: Valori): { valori: Valori; utile: number } {
function stock(year: number, month: number, utileCumulato: number): Valori {
const s = STAGIONE[month - 1]
const mesi = mesiDaInizio(year, month)
// Nel 2026 i crediti verso piattaforme e catering crescono più dei ricavi e
// il magazzino un po' meno: la vista del capitale circolante ha così
// qualcosa da raccontare rispetto all'anno prima.
const crediti = year >= 2026 ? 1.45 : 1
const scorte = year >= 2026 ? 1.12 : 1

const attivo: Valori = {
'10.01': 15_000,
'11.01': 180_000,
'11.09': 60_000 + 1_800 * mesi,
'20.01': 9_000 * (0.85 + 0.15 * s),
'21.01': 6_500 * s,
'20.01': 9_000 * (0.85 + 0.15 * s) * scorte,
'21.01': 6_500 * s * crediti,
'21.02': 1_500,
'21.03': 2_000,
'22.01': 38_000 + 0.55 * utileCumulato
Expand Down
Loading