production

This commit is contained in:
2026-08-28 11:13:15 +05:30
parent d7348e253f
commit 5723d373b2
162 changed files with 17924 additions and 7026 deletions

View File

@@ -66,6 +66,7 @@ Future<void> migrateLegacyStores() async {
_kRejectedOrderIdsKeyBase,
_kCompletedBookingsKeyBase,
_kSkippedBookingsKeyBase,
_kArrivedOrderIdsKeyBase,
_kCollectedOrderIdsKeyBase,
_kConsignmentIdsKeyBase,
_kOutForDeliveryKeyBase,
@@ -111,6 +112,7 @@ Future<void> clearScopedStores() async {
_kRejectedOrderIdsKeyBase,
_kCompletedBookingsKeyBase,
_kSkippedBookingsKeyBase,
_kArrivedOrderIdsKeyBase,
_kCollectedOrderIdsKeyBase,
_kConsignmentIdsKeyBase,
_kOutForDeliveryKeyBase,
@@ -529,6 +531,72 @@ Future<void> addAcceptedBookings(List<Map<String, dynamic>> bookings) async {
// `delivered` one, so the terminal event can be named for what it is). Until
// then a crash between the kitchen and the first door loses only the ordering,
// not the work: every order is still accepted server-side and still appears.
// ─────────────────────────────────────────────────────────────────────────
// ARRIVED — the rider is standing at the source
//
// ── Why this has to be stored at all ──
//
// Every other rung the rider walks is confirmed by the server and comes back
// on the next poll, so the app never had to remember it. Arrival does not:
// `POST /miler/bookings/:id/reached` answers 200 and leaves the booking on
// `Miler_Assigned` (verified in production, see MILER_API_REQUIREMENTS.md
// request 15). So the rung existed only as a field on an in-memory row, and
// the very next `_fetchQueues` — which the arrival sheet itself triggers —
// rebuilt that row from the server and put it back on ACCEPTED.
//
// The rider's report was being overwritten roughly one second after he made
// it. That is the whole of the "mark as arrived does nothing" bug: the write
// was fine, the rung was fine, and nothing kept it.
//
// So arrival is kept here, on the same footing as [_kCollectedOrderIdsKeyBase]
// and [_kOutForDeliveryKeyBase] — a local mirror of a rung the queue endpoints
// cannot yet carry. **Delete this store the day `reached` persists**, and not
// before: a local record that outranks the server is a liability the moment
// the server has the answer.
//
// It is dropped as soon as the stop moves on, so it can never outrank a rung
// the server *does* know about.
const String _kArrivedOrderIdsKeyBase = 'arrived_order_ids';
/// Order ids the rider has marked arrived and not yet collected.
Future<Set<String>> getArrivedOrderIds() async {
final prefs = await SharedPreferences.getInstance();
return (prefs.getStringList(await _scopedKey(_kArrivedOrderIdsKeyBase)) ?? [])
.toSet();
}
/// Records an arrival. Called with every stop at the counter he walked up to.
Future<void> addArrivedOrderIds(List<String> ids) async {
final clean = ids.where((s) => s.isNotEmpty).toSet();
if (clean.isEmpty) return;
final prefs = await SharedPreferences.getInstance();
final existing =
(prefs.getStringList(await _scopedKey(_kArrivedOrderIdsKeyBase)) ?? [])
.toSet();
existing.addAll(clean);
await prefs.setStringList(
await _scopedKey(_kArrivedOrderIdsKeyBase),
existing.toList(),
);
}
/// Drops ids the moment they leave the arrived rung — collected, skipped,
/// cancelled or rejected. Without this the set outlives the stop and yesterday's
/// arrival pins today's row to ARRIVED.
Future<void> removeArrivedOrderIds(List<String> ids) async {
final drop = ids.where((s) => s.isNotEmpty).toSet();
if (drop.isEmpty) return;
final prefs = await SharedPreferences.getInstance();
final remaining =
(prefs.getStringList(await _scopedKey(_kArrivedOrderIdsKeyBase)) ?? [])
.where((id) => !drop.contains(id))
.toList();
await prefs.setStringList(
await _scopedKey(_kArrivedOrderIdsKeyBase),
remaining,
);
}
const String _kCollectedOrderIdsKeyBase = 'collected_order_ids';
/// Order ids the rider has loaded and is carrying.