calendario delle consegne (griglia del mese + ordini del giorno) e fedelta' dei dati: gli ordini conservano nota del cliente e origine, la consegna scala le uova dal magazzino come nell'originale
This commit is contained in:
@@ -30,7 +30,7 @@ Android (AdMob/File picker/Share). Nessun account, nessun cloud, come l'original
|
|||||||
| Backup e ripristino (JSON dell'app Android) | fatto |
|
| Backup e ripristino (JSON dell'app Android) | fatto |
|
||||||
| Scelta lingua a runtime (4 lingue) | fatta |
|
| Scelta lingua a runtime (4 lingue) | fatta |
|
||||||
| Introduzione (primo avvio e dalle impostazioni) | fatta |
|
| Introduzione (primo avvio e dalle impostazioni) | fatta |
|
||||||
| Calendario delle consegne | da fare |
|
| Calendario delle consegne | fatto |
|
||||||
|
|
||||||
## Struttura
|
## Struttura
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ src/datastore.h/.cpp # dati in un JSON in AppConfigLocation + backup nel
|
|||||||
qml/harbour-henflow.qml # ApplicationWindow: initialPage + cover
|
qml/harbour-henflow.qml # ApplicationWindow: initialPage + cover
|
||||||
qml/pages/MainPage.qml # Home: produzione stimata, coda ordini, menu (ordine, clienti, impostazioni)
|
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/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/SettingsPage.qml # indice delle impostazioni
|
||||||
qml/pages/BreedsPage.qml # razze disponibili (attiva/disattiva con conferma)
|
qml/pages/BreedsPage.qml # razze disponibili (attiva/disattiva con conferma)
|
||||||
qml/pages/CoopPage.qml # mio pollaio: conteggi e produzione per razza
|
qml/pages/CoopPage.qml # mio pollaio: conteggi e produzione per razza
|
||||||
@@ -135,7 +136,7 @@ scritto esattamente come lo esporta l'app Android.
|
|||||||
| `OnboardingPage` | 4 passi introduttivi | `IntroPage` (primo avvio + dalle impostazioni) |
|
| `OnboardingPage` | 4 passi introduttivi | `IntroPage` (primo avvio + dalle impostazioni) |
|
||||||
| `ModeSelectionPage` | modalita' allevatore / cliente | non portata: solo allevatore, come deciso |
|
| `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` | card produzione stimata, ordini con stato e azioni, FAB *New order* | `MainPage` + `OrderPage` |
|
||||||
| `HomePage` tab *Calendar* | calendario delle prenotazioni | da fare |
|
| `HomePage` tab *Calendar* | calendario delle prenotazioni | `CalendarPage` |
|
||||||
| `SettingsPage` tab *Available breeds* | catalogo con foto e spunta, conferma alla disattivazione | `BreedsPage` |
|
| `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` |
|
| `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) |
|
| `BreedDetailPage` + `AddHenDialog` | galline della razza, eta' in mesi, stato deposizione | `BreedDetailPage` + `HenPage` (data con tre selettori) |
|
||||||
@@ -166,9 +167,7 @@ git archive --format=tar.gz --prefix=harbour-henflow-0.2.0/ -o harbour-henflow-0
|
|||||||
|
|
||||||
## Aperti
|
## Aperti
|
||||||
|
|
||||||
1. **Calendario delle consegne** (il tab *Calendar* dell'originale): griglia del
|
1. **Verifica su device**: la UI Silica non e' verificabile senza SDK/dispositivo
|
||||||
mese con gli ordini per giorno e l'elenco del giorno scelto.
|
|
||||||
2. **Verifica su device**: la UI Silica non e' verificabile senza SDK/dispositivo
|
|
||||||
(qui si verificano motore, traduzioni, catalogo, livello dati, chiavi e sintassi
|
(qui si verificano motore, traduzioni, catalogo, livello dati, chiavi e sintassi
|
||||||
della QML, ma non il comportamento a schermo).
|
della QML, ma non il comportamento a schermo).
|
||||||
|
|
||||||
@@ -185,3 +184,11 @@ git archive --format=tar.gz --prefix=harbour-henflow-0.2.0/ -o harbour-henflow-0
|
|||||||
non ha il plugin webp. Riconvertito in JPEG.
|
non ha il plugin webp. Riconvertito in JPEG.
|
||||||
- Niente annunci (AdMob non esiste su Sailfish) e niente `share_plus`/`file_picker`:
|
- Niente annunci (AdMob non esiste su Sailfish) e niente `share_plus`/`file_picker`:
|
||||||
export/import JSON in Download.
|
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).
|
||||||
|
|||||||
+21
-9
@@ -137,15 +137,26 @@ function accrueStock(stock, breedSettings, catalog, daysPassed) {
|
|||||||
// --------------------------------------------------------- prenotazioni
|
// --------------------------------------------------------- prenotazioni
|
||||||
|
|
||||||
function cloneReservation(r) {
|
function cloneReservation(r) {
|
||||||
return {
|
// Copia TUTTI i campi: l'ordine dell'app Android ne ha anche di piu'
|
||||||
id: r.id,
|
// (source, clientNote) e il ricalcolo delle date non deve perderli.
|
||||||
customerName: r.customerName,
|
var out = {}, k;
|
||||||
breedId: r.breedId,
|
for (k in r) {
|
||||||
eggsRequested: r.eggsRequested,
|
if (r.hasOwnProperty(k)) out[k] = r[k];
|
||||||
createdAt: r.createdAt,
|
}
|
||||||
deliveryDate: r.deliveryDate,
|
return out;
|
||||||
isDelivered: r.isDelivered
|
}
|
||||||
};
|
|
||||||
|
// Consegna: l'app originale, quando un ordine viene segnato come consegnato,
|
||||||
|
// SCALA le uova dal magazzino di quella razza; annullando la consegna le
|
||||||
|
// rimette. Restituisce un magazzino nuovo (l'input non viene toccato).
|
||||||
|
function applyDelivery(stock, reservation, delivered) {
|
||||||
|
var out = {}, k;
|
||||||
|
for (k in stock) {
|
||||||
|
if (stock.hasOwnProperty(k)) out[k] = stock[k];
|
||||||
|
}
|
||||||
|
var eggs = reservation.eggsRequested || 0;
|
||||||
|
out[reservation.breedId] = (out[reservation.breedId] || 0) + (delivered ? -eggs : eggs);
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pendingEggs(reservations, breedId) {
|
function pendingEggs(reservations, breedId) {
|
||||||
@@ -268,6 +279,7 @@ if (typeof module !== "undefined" && module.exports) {
|
|||||||
isProductive: isProductive,
|
isProductive: isProductive,
|
||||||
accrueStock: accrueStock,
|
accrueStock: accrueStock,
|
||||||
pendingEggs: pendingEggs,
|
pendingEggs: pendingEggs,
|
||||||
|
applyDelivery: applyDelivery,
|
||||||
estimateDeliveryDate: estimateDeliveryDate,
|
estimateDeliveryDate: estimateDeliveryDate,
|
||||||
refreshDeliveryDates: refreshDeliveryDates,
|
refreshDeliveryDates: refreshDeliveryDates,
|
||||||
statusKey: statusKey,
|
statusKey: statusKey,
|
||||||
|
|||||||
@@ -0,0 +1,407 @@
|
|||||||
|
import QtQuick 2.2
|
||||||
|
import Sailfish.Silica 1.0
|
||||||
|
import "../js/henflow.js" as HF
|
||||||
|
import "../js/i18n.js" as I18N
|
||||||
|
import "../js/catalog.js" as Catalog
|
||||||
|
|
||||||
|
// Calendario delle consegne (il tab *Calendar* della Home dell'originale):
|
||||||
|
// griglia del mese con un puntino sui giorni che hanno ordini e, sotto,
|
||||||
|
// l'elenco degli ordini del giorno scelto.
|
||||||
|
// Nomi di mesi e giorni nella lingua del TELEFONO (Qt.locale()), come faceva
|
||||||
|
// TableCalendar nell'app Flutter: non sono nelle 126 chiavi dell'app.
|
||||||
|
// Tutto sta dentro la lista (griglia compresa): su questa Silica e' il layout
|
||||||
|
// che si comporta bene con i menu a tendina.
|
||||||
|
Page {
|
||||||
|
id: page
|
||||||
|
allowedOrientations: Orientation.All
|
||||||
|
|
||||||
|
property string lang: "en"
|
||||||
|
property var reservations: []
|
||||||
|
property var orders: []
|
||||||
|
property string selectedIso: ""
|
||||||
|
property int viewYear: 0
|
||||||
|
property int viewMonth: 1
|
||||||
|
property string todayIso: ""
|
||||||
|
property string monthTitle: ""
|
||||||
|
property var weekdayNames: []
|
||||||
|
property bool ready: false
|
||||||
|
|
||||||
|
// Larghezza di una cella del calendario: 7 colonne piene.
|
||||||
|
readonly property real cellWidth: width / 7
|
||||||
|
readonly property real cellHeight: Theme.itemSizeSmall
|
||||||
|
|
||||||
|
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||||
|
|
||||||
|
ListModel { id: gridModel }
|
||||||
|
ListModel { id: ordersModel }
|
||||||
|
|
||||||
|
function isoOf(y, m, d) {
|
||||||
|
return HF.isoDay(new Date(y, m - 1, d))
|
||||||
|
}
|
||||||
|
|
||||||
|
function countFor(iso) {
|
||||||
|
var n = 0
|
||||||
|
for (var i = 0; i < page.reservations.length; i++) {
|
||||||
|
var d = HF.parseIso(page.reservations[i].deliveryDate)
|
||||||
|
if (d && HF.isoDay(d) === iso)
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildWeekdays() {
|
||||||
|
var names = []
|
||||||
|
for (var i = 1; i <= 7; i++)
|
||||||
|
names.push(Qt.locale().dayName(i, Locale.ShortFormat))
|
||||||
|
page.weekdayNames = names
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTitle() {
|
||||||
|
page.monthTitle = Qt.locale().monthName(page.viewMonth, Locale.LongFormat)
|
||||||
|
+ " " + page.viewYear
|
||||||
|
}
|
||||||
|
|
||||||
|
function reload() {
|
||||||
|
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||||
|
page.reservations = appData.reservations()
|
||||||
|
|
||||||
|
var today = HF.todayMidnight()
|
||||||
|
page.todayIso = HF.isoDay(today)
|
||||||
|
if (page.viewYear === 0) {
|
||||||
|
page.viewYear = today.getFullYear()
|
||||||
|
page.viewMonth = today.getMonth() + 1
|
||||||
|
}
|
||||||
|
if (page.selectedIso.length === 0)
|
||||||
|
page.selectedIso = page.todayIso
|
||||||
|
|
||||||
|
page.buildWeekdays()
|
||||||
|
page.updateTitle()
|
||||||
|
page.buildGrid()
|
||||||
|
page.buildOrders()
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildGrid() {
|
||||||
|
gridModel.clear()
|
||||||
|
|
||||||
|
var first = new Date(page.viewYear, page.viewMonth - 1, 1)
|
||||||
|
// settimana che inizia di lunedi: getDay() da' 0 = domenica
|
||||||
|
var offset = (first.getDay() + 6) % 7
|
||||||
|
var daysInMonth = new Date(page.viewYear, page.viewMonth, 0).getDate()
|
||||||
|
|
||||||
|
for (var i = 0; i < offset; i++)
|
||||||
|
gridModel.append({ "day": 0, "iso": "", "count": 0, "today": false, "selected": false })
|
||||||
|
|
||||||
|
for (var d = 1; d <= daysInMonth; d++) {
|
||||||
|
var iso = page.isoOf(page.viewYear, page.viewMonth, d)
|
||||||
|
gridModel.append({
|
||||||
|
"day": d,
|
||||||
|
"iso": iso,
|
||||||
|
"count": page.countFor(iso),
|
||||||
|
"today": iso === page.todayIso,
|
||||||
|
"selected": iso === page.selectedIso
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
while (gridModel.count % 7 !== 0)
|
||||||
|
gridModel.append({ "day": 0, "iso": "", "count": 0, "today": false, "selected": false })
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOrders() {
|
||||||
|
ordersModel.clear()
|
||||||
|
var today = HF.todayMidnight()
|
||||||
|
for (var i = 0; i < page.reservations.length; i++) {
|
||||||
|
var r = page.reservations[i]
|
||||||
|
var d = HF.parseIso(r.deliveryDate)
|
||||||
|
if (!d || HF.isoDay(d) !== page.selectedIso)
|
||||||
|
continue
|
||||||
|
|
||||||
|
var days = HF.dayDiff(today, d)
|
||||||
|
var status
|
||||||
|
if (r.isDelivered) status = page.tr("ready_delivery")
|
||||||
|
else if (days < 0) status = page.tr("expired")
|
||||||
|
else if (days === 0) status = page.tr("today")
|
||||||
|
else if (days === 1) status = page.tr("in_day")
|
||||||
|
else status = page.tr("in_days", days)
|
||||||
|
|
||||||
|
var breed = Catalog.breedFromId(r.breedId)
|
||||||
|
ordersModel.append({
|
||||||
|
"id": r.id,
|
||||||
|
"customer": r.customerName,
|
||||||
|
"breed": breed ? page.tr(breed.name) : r.breedId,
|
||||||
|
"eggs": r.eggsRequested,
|
||||||
|
"note": r.clientNote ? r.clientNote : "",
|
||||||
|
"status": status,
|
||||||
|
"late": (!r.isDelivered && days < 0),
|
||||||
|
"delivered": r.isDelivered
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shiftMonth(delta) {
|
||||||
|
var m = page.viewMonth + delta
|
||||||
|
var y = page.viewYear
|
||||||
|
if (m < 1) {
|
||||||
|
m = 12
|
||||||
|
y--
|
||||||
|
} else if (m > 12) {
|
||||||
|
m = 1
|
||||||
|
y++
|
||||||
|
}
|
||||||
|
page.viewMonth = m
|
||||||
|
page.viewYear = y
|
||||||
|
page.updateTitle()
|
||||||
|
page.buildGrid()
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectDay(iso) {
|
||||||
|
if (iso.length === 0)
|
||||||
|
return
|
||||||
|
page.selectedIso = iso
|
||||||
|
page.buildGrid()
|
||||||
|
page.buildOrders()
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToday() {
|
||||||
|
var today = HF.todayMidnight()
|
||||||
|
page.viewYear = today.getFullYear()
|
||||||
|
page.viewMonth = today.getMonth() + 1
|
||||||
|
page.selectedIso = page.todayIso
|
||||||
|
page.updateTitle()
|
||||||
|
page.buildGrid()
|
||||||
|
page.buildOrders()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openOrder(index) {
|
||||||
|
var id = page.orders[index].id
|
||||||
|
var list = appData.reservations()
|
||||||
|
var reservation = null
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
if (list[i].id === id)
|
||||||
|
reservation = list[i]
|
||||||
|
}
|
||||||
|
pageStack.push(Qt.resolvedUrl("OrderPage.qml"),
|
||||||
|
{ "reservation": reservation,
|
||||||
|
"onSaved": function () { page.reload() } })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consegnato: come l'originale, segnare la consegna SCALA le uova dal
|
||||||
|
// magazzino (e annullarla le rimette).
|
||||||
|
function toggleDelivered(index) {
|
||||||
|
var id = page.orders[index].id
|
||||||
|
var list = appData.reservations().slice()
|
||||||
|
for (var i = 0; i < list.length; i++) {
|
||||||
|
if (list[i].id === id) {
|
||||||
|
list[i].isDelivered = !list[i].isDelivered
|
||||||
|
appData.saveWarehouseStock(
|
||||||
|
HF.applyDelivery(appData.warehouseStock(), list[i], list[i].isDelivered))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
appData.saveReservations(list)
|
||||||
|
page.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: page.reload()
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status !== PageStatus.Active)
|
||||||
|
return
|
||||||
|
if (page.ready)
|
||||||
|
page.reload()
|
||||||
|
page.ready = true
|
||||||
|
}
|
||||||
|
|
||||||
|
SilicaListView {
|
||||||
|
id: dayList
|
||||||
|
anchors.fill: parent
|
||||||
|
model: ordersModel
|
||||||
|
|
||||||
|
header: Column {
|
||||||
|
width: dayList.width
|
||||||
|
|
||||||
|
PageHeader { title: page.tr("calendar") }
|
||||||
|
|
||||||
|
// navigazione fra i mesi
|
||||||
|
Row {
|
||||||
|
x: Theme.horizontalPageMargin
|
||||||
|
width: dayList.width - 2 * Theme.horizontalPageMargin
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
id: prevButton
|
||||||
|
icon.source: "image://theme/icon-m-left"
|
||||||
|
onClicked: page.shiftMonth(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.verticalCenter: prevButton.verticalCenter
|
||||||
|
width: parent.width - 2 * (prevButton.width + parent.spacing)
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: page.monthTitle
|
||||||
|
color: Theme.highlightColor
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
truncationMode: TruncationMode.Fade
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton {
|
||||||
|
icon.source: "image://theme/icon-m-right"
|
||||||
|
onClicked: page.shiftMonth(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// intestazione dei giorni della settimana
|
||||||
|
Row {
|
||||||
|
x: Theme.horizontalPageMargin
|
||||||
|
width: dayList.width - 2 * Theme.horizontalPageMargin
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: page.weekdayNames.length
|
||||||
|
delegate: Label {
|
||||||
|
width: page.cellWidth
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: page.weekdayNames[index]
|
||||||
|
color: Theme.secondaryColor
|
||||||
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
|
truncationMode: TruncationMode.Fade
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// griglia del mese
|
||||||
|
Grid {
|
||||||
|
x: Theme.horizontalPageMargin
|
||||||
|
width: dayList.width - 2 * Theme.horizontalPageMargin
|
||||||
|
height: (gridModel.count / 7) * page.cellHeight
|
||||||
|
columns: 7
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: gridModel
|
||||||
|
|
||||||
|
delegate: BackgroundItem {
|
||||||
|
id: cell
|
||||||
|
width: page.cellWidth
|
||||||
|
height: page.cellHeight
|
||||||
|
|
||||||
|
onClicked: page.selectDay(model.iso)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: Math.min(parent.width, parent.height) - Theme.paddingMedium
|
||||||
|
height: width
|
||||||
|
radius: width / 2
|
||||||
|
color: model.selected ? Theme.highlightBackgroundColor
|
||||||
|
: (model.today ? Theme.rgba(Theme.highlightBackgroundColor, 0.35)
|
||||||
|
: "transparent")
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: model.day > 0 ? String(model.day) : ""
|
||||||
|
color: model.selected ? Theme.highlightColor
|
||||||
|
: (model.day > 0 ? Theme.primaryColor : Theme.secondaryColor)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// puntino: quel giorno ha ordini
|
||||||
|
Rectangle {
|
||||||
|
visible: model.count > 0
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: Theme.paddingSmall
|
||||||
|
}
|
||||||
|
width: Theme.paddingSmall
|
||||||
|
height: width
|
||||||
|
radius: width / 2
|
||||||
|
color: Theme.highlightColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { width: parent.width; height: Theme.paddingMedium }
|
||||||
|
|
||||||
|
SectionHeader {
|
||||||
|
text: HF.formatDay(page.selectedIso) + " (" + ordersModel.count + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ListItem {
|
||||||
|
id: orderItem
|
||||||
|
contentHeight: orderText.height + Theme.paddingMedium
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: orderText
|
||||||
|
x: Theme.horizontalPageMargin
|
||||||
|
y: Theme.paddingMedium
|
||||||
|
width: parent.width - 2 * Theme.horizontalPageMargin - deliveredSwitch.width
|
||||||
|
- Theme.paddingMedium
|
||||||
|
spacing: Theme.paddingSmall
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
text: model.customer
|
||||||
|
color: orderItem.highlighted ? Theme.highlightColor : Theme.primaryColor
|
||||||
|
truncationMode: TruncationMode.Fade
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
text: page.tr("breed") + ": " + model.breed + " · " + page.tr("eggs_req") + ": " + model.eggs
|
||||||
|
color: Theme.secondaryColor
|
||||||
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
|
truncationMode: TruncationMode.Fade
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
text: model.status
|
||||||
|
color: model.late ? Theme.errorColor : Theme.secondaryColor
|
||||||
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
|
truncationMode: TruncationMode.Fade
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
visible: model.note.length > 0
|
||||||
|
text: page.tr("client_note") + ": " + model.note
|
||||||
|
color: Theme.secondaryColor
|
||||||
|
font.pixelSize: Theme.fontSizeExtraSmall
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Switch {
|
||||||
|
id: deliveredSwitch
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: Theme.horizontalPageMargin
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
checked: model.delivered
|
||||||
|
onClicked: page.toggleDelivered(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: page.openOrder(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewPlaceholder {
|
||||||
|
enabled: ordersModel.count === 0
|
||||||
|
text: page.tr("no_reservations")
|
||||||
|
}
|
||||||
|
|
||||||
|
PullDownMenu {
|
||||||
|
MenuItem {
|
||||||
|
text: page.tr("today")
|
||||||
|
onClicked: page.goToday()
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: page.tr("new_order")
|
||||||
|
onClicked: pageStack.push(Qt.resolvedUrl("OrderPage.qml"),
|
||||||
|
{ "reservation": null,
|
||||||
|
"onSaved": function () { page.reload() } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalScrollDecorator {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,6 +111,10 @@ Page {
|
|||||||
if (index < 0 || index >= list.length)
|
if (index < 0 || index >= list.length)
|
||||||
return
|
return
|
||||||
list[index].isDelivered = !list[index].isDelivered
|
list[index].isDelivered = !list[index].isDelivered
|
||||||
|
// Come l'originale: segnare la consegna SCALA le uova dal magazzino
|
||||||
|
// (annullarla le rimette).
|
||||||
|
appData.saveWarehouseStock(
|
||||||
|
HF.applyDelivery(appData.warehouseStock(), list[index], list[index].isDelivered))
|
||||||
page.reservations = list
|
page.reservations = list
|
||||||
appData.saveReservations(list)
|
appData.saveReservations(list)
|
||||||
page.rebuildList()
|
page.rebuildList()
|
||||||
@@ -281,6 +285,10 @@ Page {
|
|||||||
text: page.tr("customers_list")
|
text: page.tr("customers_list")
|
||||||
onClicked: pageStack.push(Qt.resolvedUrl("CustomersPage.qml"))
|
onClicked: pageStack.push(Qt.resolvedUrl("CustomersPage.qml"))
|
||||||
}
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: page.tr("calendar")
|
||||||
|
onClicked: pageStack.push(Qt.resolvedUrl("CalendarPage.qml"))
|
||||||
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: page.tr("settings")
|
text: page.tr("settings")
|
||||||
onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
|
onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ Page {
|
|||||||
if (page.selected < 0 && list.length > 0)
|
if (page.selected < 0 && list.length > 0)
|
||||||
page.selected = 0
|
page.selected = 0
|
||||||
|
|
||||||
|
// Come l'originale: un nuovo ordine parte da 6 uova.
|
||||||
|
if (!page.reservation && eggsField.text.length === 0)
|
||||||
|
eggsField.text = "6"
|
||||||
|
|
||||||
page.updatePreview()
|
page.updatePreview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,5 +120,24 @@ check("formatDay su stringa vuota", HF.formatDay(""), "");
|
|||||||
check("nowIso in formato ISO", /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.000$/.test(HF.nowIso()), true);
|
check("nowIso in formato ISO", /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.000$/.test(HF.nowIso()), true);
|
||||||
check("newId unico", HF.newId() !== HF.newId(), true);
|
check("newId unico", HF.newId() !== HF.newId(), true);
|
||||||
|
|
||||||
|
// --- fedelta' dei campi degli ordini (app Android) ------------------------
|
||||||
|
var withNote = { id: "n1", customerName: "Anna", breedId: "marans", eggsRequested: 2,
|
||||||
|
createdAt: "2026-04-25T09:00:00.000", deliveryDate: "2026-04-28T00:00:00.000",
|
||||||
|
isDelivered: false, source: "client", clientNote: "guscio chiaro" };
|
||||||
|
var kept = HF.refreshDeliveryDates([withNote], {}, BREEDS, CATALOG, TODAY);
|
||||||
|
check("nota del cliente conservata nel ricalcolo delle date", kept[0].clientNote, "guscio chiaro");
|
||||||
|
check("origine dell'ordine conservata", kept[0].source, "client");
|
||||||
|
|
||||||
|
// --- consegna: le uova escono dal magazzino -------------------------------
|
||||||
|
var stock0 = { "marans": 10, "araucana": 3 };
|
||||||
|
var afterDelivery = HF.applyDelivery(stock0, { breedId: "marans", eggsRequested: 4 }, true);
|
||||||
|
check("consegna: uova scalate dal magazzino della razza", afterDelivery.marans, 6);
|
||||||
|
check("consegna: le altre razze non si toccano", afterDelivery.araucana, 3);
|
||||||
|
check("consegna: magazzino di partenza non modificato", stock0.marans, 10);
|
||||||
|
check("consegna annullata: uova rimesse nel magazzino",
|
||||||
|
HF.applyDelivery(afterDelivery, { breedId: "marans", eggsRequested: 4 }, false).marans, 10);
|
||||||
|
check("consegna su razza senza scorte: il magazzino va sotto zero",
|
||||||
|
HF.applyDelivery({}, { breedId: "olive", eggsRequested: 3 }, true).olive, -3);
|
||||||
|
|
||||||
console.log(failures === 0 ? "\ntutti i controlli superati" : "\n" + failures + " controlli falliti");
|
console.log(failures === 0 ? "\ntutti i controlli superati" : "\n" + failures + " controlli falliti");
|
||||||
process.exit(failures === 0 ? 0 : 1);
|
process.exit(failures === 0 ? 0 : 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user