Files
harbour-henflow/README.md
T

195 lines
11 KiB
Markdown

# harbour-henflow
Porting **nativo Sailfish OS** dell'app Android *Henflow* (`it.carlobaratto.henflow`).
UI in QML/Silica, calcolo e dati locali, pubblicazione prevista sullo
**store Jolla Harbour**.
Il porting **non** usa il runtime Flutter community (`flutter-sailfishos`):
quello resta una via OpenRepos, non Harbour-safe, e non supporta i plugin nativi
Android (AdMob/File picker/Share). Nessun account, nessun cloud, come l'originale.
## Sorgenti di riferimento
- Flutter (originale): `workspace/henflow-flutter` — export da Nextcloud, repo Gitea `HenFlow`.
- Screenshot dello store usati come riferimento UI.
## Stato
| parte | stato |
|---|---|
| Packaging (spec RPM, desktop SailJail, icone, i18n) | pronto |
| Livello dati C++ (`src/datastore.*`, JSON compatibile con l'app Android) | fatto e verificato |
| Motore di calcolo (`qml/js/henflow.js`) | portato e verificato con node |
| Traduzioni 4 lingue (`qml/js/i18n.js`, 126 chiavi) | portate e verificate con node |
| Catalogo razze con foto (`qml/js/catalog.js`) | portato e verificato con node |
| Home con produzione stimata e coda ordini | fatta |
| Scheda ordine (nuovo/modifica, data calcolata) | fatta |
| Cover con produzione e ordini pendenti | fatta |
| Razze disponibili, mio pollaio, registro galline | fatte |
| Clienti (anagrafica) | fatta |
| Backup e ripristino (JSON dell'app Android) | fatto |
| Scelta lingua a runtime (4 lingue) | fatta |
| Introduzione (primo avvio e dalle impostazioni) | fatta |
| Calendario delle consegne | fatto |
## Struttura
```
harbour-henflow.pro # QT += quick qml, INSTALLS icone + foto razze
rpm/harbour-henflow.spec # lrelease + desktop cercati con find (layout sorgente variabile)
rpm/harbour-henflow.desktop # profilo [X-Sailjail] + permesso Documents (backup)
src/main.cpp # SailfishApp, context property appSettings/appData/appVersion
src/settings.h/.cpp # QSettings su path esplicito AppConfigLocation + sync() (lingua, intro vista)
src/datastore.h/.cpp # dati in un JSON in AppConfigLocation + backup nel formato dell'app Android
qml/harbour-henflow.qml # ApplicationWindow: initialPage + cover
qml/pages/MainPage.qml # Home: produzione stimata, coda ordini, menu (ordine, clienti, impostazioni)
qml/pages/OrderPage.qml # nuovo/modifica ordine con anteprima della data di consegna
qml/pages/CalendarPage.qml # calendario delle consegne: griglia del mese + ordini del giorno
qml/pages/SettingsPage.qml # indice delle impostazioni
qml/pages/BreedsPage.qml # razze disponibili (attiva/disattiva con conferma)
qml/pages/CoopPage.qml # mio pollaio: conteggi e produzione per razza
qml/pages/BreedDetailPage.qml# scheda razza + registro galline (deposizione, elimina)
qml/pages/HenPage.qml # nuova/modifica gallina: nome, data di nascita, deposizione
qml/pages/CustomersPage.qml # anagrafica clienti
qml/pages/CustomerPage.qml # nuovo/modifica cliente
qml/pages/BackupPage.qml # esporta in Documenti / copia negli appunti / ripristina da JSON
qml/pages/LanguagePage.qml # lingua (EN/IT/FR/DE) applicata a runtime
qml/pages/IntroPage.qml # introduzione in 4 passi
qml/pages/AboutPage.qml # informazioni e versione
qml/cover/CoverPage.qml # produzione del giorno e ordini pendenti
qml/js/henflow.js # motore di calcolo (ES5, testabile con node)
qml/js/i18n.js # 126 chiavi x 4 lingue + t(chiave, arg, lingua)
qml/js/catalog.js # 7 razze: nome/descrizione/colore uova/foto
assets/breeds/*.jpg # foto delle razze (installate nel datadir)
tools/gen_from_flutter.py # rigenera i18n.js e catalog.js dai sorgenti Flutter
tools/check_qml.py # controlli statici: chiavi, simboli JS, pagine, componenti
tests/run.sh # motore + traduzioni (node) + controlli QML + qmllint
tests/cpp_test.sh # livello dati (Qt 5.15 locale, config in cartella temporanea)
icons/make_icons.py # icone 86/108/128/172 dall'artwork originale
translations/*.ts # stringhe statiche QML (il testo dell'app viene da i18n.js)
```
## Test
```sh
sh tests/run.sh # motore di calcolo, traduzioni e catalogo (node), controlli
# statici della QML (chiavi, simboli JS, pagine, componenti) e qmllint
sh tests/cpp_test.sh # livello dati: compila src/datastore.cpp col Qt locale e prova
# persistenza, rilettura, compatibilita' delle chiavi, backup, file corrotto
```
I controlli statici della QML servono perche' la UI non e' eseguibile senza SDK:
prima di consegnare verificano che ogni chiave di traduzione usata esista in tutte
e quattro le lingue, che le funzioni del motore chiamate esistano, che le pagine
aperte con `pageStack` esistano davvero e che non si usino componenti inesistenti
(es. `Toast`, che in Sailfish non c'e'). `qmllint` aggiunge la sintassi: gli errori
di sintassi sono proprio quelli che sul device fanno fallire la pagina con
"Impossibile caricare la pagina".
Il testo dell'app vive in `i18n.js` (come il TranslationService dell'originale), quindi
la lingua cambia **a runtime** senza riavviare l'app: cosa che i `.ts` di Qt 5.6 non
permettono. La lingua predefinita segue quella del telefono.
## Motore di calcolo (portato fedelmente da `lib/main.dart`)
- **Produzione stimata** per razza = galline in deposizione x uova/gallina/giorno
(catalogo: tutte 1.0). Una gallina conta da **6 mesi** in su (`_ageInMonths`).
- **Magazzino**: all'apertura, i giorni passati dall'ultimo aggiornamento
aggiungono la produzione giornaliera alle scorte di ogni razza attiva.
- **Coda FIFO**: ordini in ordine di creazione; ognuno consuma le scorte virtuali
della sua razza, chi le trova consegna oggi, gli altri aspettano
`ceil(uova_mancanti / produzione)` giorni. Gli ordini gia' consegnati non
consumano scorte.
- **Stima per un nuovo ordine**: `ceil((pendenti + richieste - scorte) / produzione)`.
Le date sono sempre normalizzate a `YYYY-MM-DD` e le funzioni non mutano l'input.
## Modello dati (fedele a `StorageService`, SharedPreferences + JSON)
| chiave | contenuto |
|---|---|
| `breed_settings_v2` | `[{breedId, hens:[{id, name, birthDate, isLaying}]}]` |
| `reservations_v2` | `[{id, customerName, breedId, eggsRequested, createdAt, deliveryDate, isDelivered}]` |
| `customers_v1` | `[{id, firstName, lastName, phoneNumber}]` |
| `warehouse_stock_v1` | `{breedId: uova}` |
| `last_production_update_v1` | data ISO dell'ultimo accrual |
Nel porting: un unico file JSON in `~/.config/harbour/henflow/` (nessuna
dipendenza extra, nessun driver SQL da verificare sul device) con la stessa
struttura, cosi' i due formati restano leggibili.
### Backup (formato dell'app Android)
L'export e il ripristino usano le chiavi **dell'app Android** — `breedSettings`,
`reservations`, `customers` (camelCase) — non quelle interne del file: un backup
esportato sul telefono Android si ripristina qui e viceversa. Il file viene
scritto in `~/Documents` (serve il permesso `Documents` nel profilo SailJail);
in alternativa il JSON si copia negli appunti. Scorte e data dell'ultimo
aggiornamento non stanno nel backup: al ripristino restano quelli correnti, cosi'
non si azzera il magazzino. `tests/cpp_test.sh` prova il round-trip con un backup
scritto esattamente come lo esporta l'app Android.
## Schermate mappate (da sorgenti + screenshot)
| schermata Flutter | contenuto | porting |
|---|---|---|
| `OnboardingPage` | 4 passi introduttivi | `IntroPage` (primo avvio + dalle impostazioni) |
| `ModeSelectionPage` | modalita' allevatore / cliente | non portata: solo allevatore, come deciso |
| `HomePage` | card produzione stimata, ordini con stato e azioni, FAB *New order* | `MainPage` + `OrderPage` |
| `HomePage` tab *Calendar* | calendario delle prenotazioni | `CalendarPage` |
| `SettingsPage` tab *Available breeds* | catalogo con foto e spunta, conferma alla disattivazione | `BreedsPage` |
| `SettingsPage` tab *My coop* | razze allevate, galline registrate/in deposizione, produzione | `CoopPage` + `BreedDetailPage` |
| `BreedDetailPage` + `AddHenDialog` | galline della razza, eta' in mesi, stato deposizione | `BreedDetailPage` + `HenPage` (data con tre selettori) |
| `CustomersPage` + dialoghi | anagrafica clienti | `CustomersPage` + `CustomerPage` |
| `AddReservationSheet` | nuovo/modifica ordine | `OrderPage` |
| `SettingsPage` backup | esporta su file / importa da JSON | `BackupPage` (formato identico all'Android) |
| `AboutPage` | info app | `AboutPage` |
| `CoopIcon` (CustomPainter) | icona pollaio disegnata a mano | non portata: la Home usa la card con la produzione |
Differenze da tenere presenti: **niente annunci** (AdMob non esiste su Sailfish),
**share/export** via file JSON in Download invece di `share_plus`/`file_picker`.
## Build e deploy
Sul PC con l'SDK Sailfish (qui non c'e' sfdk):
```sh
sfdk target use SailfishOS-5.0.0.62-aarch64 # o il target del tuo device
sfdk build # RPM in build-*/RPMS/
sfdk deploy # oppure Qt Creator
```
Tarball per lo store (con `--prefix`, esclude .git e i file non tracciati):
```sh
git archive --format=tar.gz --prefix=harbour-henflow-0.2.0/ -o harbour-henflow-0.2.0.tar.gz HEAD
```
## Aperti
1. **Verifica su device**: la UI Silica non e' verificabile senza SDK/dispositivo
(qui si verificano motore, traduzioni, catalogo, livello dati, chiavi e sintassi
della QML, ma non il comportamento a schermo).
## Correzioni rispetto all'originale (bug trovati nei sorgenti Flutter)
- `app_language.dart` e' una copia **vecchia** della tabella traduzioni (111 chiavi
contro 126), usata solo da `lib/dialogs/confirm_dialogs.dart`, e ha gli a capo
scritti `\\n`: quei dialoghi mostrano un backslash visibile. Il porting estrae
dalla tabella viva in `main.dart`.
- Il francese ha 2 chiavi in meno (`name_generic`, `desc_generic`) e `t()` non ha
fallback: in francese la razza "generic" usciva come testo `name_generic`.
Nel porting i buchi sono colmati dall'inglese e un test lo verifica.
- `assets/breeds/olive.jpg` era un **WEBP** con estensione .jpg: Qt 5.6 su Sailfish
non ha il plugin webp. Riconvertito in JPEG.
- Niente annunci (AdMob non esiste su Sailfish) e niente `share_plus`/`file_picker`:
export/import JSON in Download.
- Il tab *Calendar* ha i messaggi del dialogo di consegna **scritti a mano in
italiano** ("Conferma Consegna", ...): non tradotti in nessuna delle 4 lingue.
Nel porting la consegna si segna con l'interruttore della riga, come nella Home.
- Gli ordini dell'app Android hanno anche `clientNote` e `source`: il ricalcolo
della coda ricopia **tutti** i campi, cosi' un ordine che arriva dall'Android
non perde la nota del cliente (c'e' un test apposta).
- La consegna scala le uova dal magazzino della razza, come nell'originale; nel
porting annullarla le rimette (l'originale non prevedeva l'annulla).