# EasyForma

SaaS per enti di formazione: aule virtuali BigBlueButton a pacchetti, registro presenze automatico e invio dati a Forma.Temp.
Vedi `docs/ARCHITECTURE.md` per il disegno del sistema; vetrina pubblica su `/`, `/prezzi`, `/privacy`, `/termini` (design in `docs/design-system.md`).

## Requisiti

PHP 8.4+ (consigliato 8.5), Composer, Node 22+, SQLite (sviluppo).

## Setup

```bash
composer install
cp .env.example .env
php artisan key:generate
touch database/database.sqlite
php artisan migrate --seed      # crea i pacchetti e, in locale, i dati demo (demo@easyforma.test / password)
npm install
composer run dev                # server + queue + vite + logs
```

## Comandi utili

```bash
composer test                   # pint --test, phpstan, phpunit
php artisan meetings:refresh    # sincronizza le lezioni in corso con BBB (lo scheduler lo fa ogni 3 minuti)
php artisan packages:sync-stripe # crea e riallinea prodotti/prezzi Stripe per i pacchetti
stripe listen --forward-to localhost:8000/stripe/webhook
```

## Variabili d'ambiente

- `BBB_DRIVER=fake|api`, `BBB_URL`, `BBB_SECRET`, `BBB_CHECKSUM_ALGORITHM=sha256`
- `FORMATEMP_DRIVER=fake|api`, `FORMATEMP_ENVIRONMENT=collaudo|produzione` (default per nuove aziende), `FORMATEMP_BASE_URL_TEST`, `FORMATEMP_BASE_URL_PROD`, `FORMATEMP_TIMEOUT`, `FORMATEMP_SESSION_TTL` (minuti di cache della sessione FTWEB, default 40), `FORMATEMP_MATCH_TOLERANCE` (minuti di tolleranza lezione/fascia, default 15), `FORMATEMP_GAP_MERGE` (disconnessioni sotto questa soglia non contano come assenza, default 15), `FORMATEMP_CREATE_LEARNERS` (creazione automatica dell'anagrafica discente, default false), `FORMATEMP_ID_TIPO_PROGETTO`, `FORMATEMP_FAKE_AUTOSEED`
- `STRIPE_KEY`, `STRIPE_SECRET`, `STRIPE_WEBHOOK_SECRET` (i prezzi di piani e ricariche stanno nella tabella `packages`, sincronizzati con `php artisan packages:sync-stripe`, che ricrea il prezzo e archivia il vecchio quando importo, valuta o ricorrenza cambiano)
- `CREDIT_HOURS_PER_CREDIT=4`, `CREDIT_PARTICIPANTS_BAND=30` — quanto vale un credito: mezza giornata d'aula, cioè 4 ore fino a 30 discenti. Un'aula costa `ceil(ore / 4) × ceil(discenti / 30)` crediti.
- `CREDITS_EXPIRE_DAYS` — giorni di validità dei crediti di una ricarica (vuoto = non scadono). I crediti di un piano scadono a fine periodo, ma quelli non usati si riportano sul periodo successivo fino a un'allowance mensile.
