From d612916fe4946eb097e71d6cc9124bddd14587cd Mon Sep 17 00:00:00 2001 From: Thiru-tenext Date: Fri, 18 Sep 2026 11:05:40 +0530 Subject: [PATCH] Session expiry, arrival geofence guard, multi-destination stops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three fixes found by running the app on a real handset against production. 1. An expired token left the app looking signed in and unable to work. MilerApi.onUnauthorized was declared and called on every 401 but never assigned, so the token was dropped and nothing else happened: the profile stayed on disk, logged_out stayed false, and the rider saw his own name over a dashboard whose every call returned 401. He reads that as "no work today". The teardown now lives in endSession() and both ways out of a session — the Log out button and the 401 path — use it. 2. Arrived was written locally even when the rider was not there. updateArrivedStatus answers false for three different things and the caller treated all of them as "the write did not land", which is only true of one. A geofence refusal and a server refusal now stop the rung and hand back the reason; a dead network still advances, as it should. 3. A multi-destination customer pickup collapsed onto one stop. GET /miler/bookings returns a row per destination once collected, all with the same bookingid and reference. Every local store keys on that id, so the accepted store deduped two of three drops away and their consignment ids were unrecoverable. orderid is now the stop key; bookingreference stays the booking's name. Cards show "Stop 2 of 3" and the receiver's own name and number rather than the sender's. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01EqVJPB9B4QuieZnBAAKgYQ --- android/app/src/main/AndroidManifest.xml | 56 +- android/key.properties | 4 - lib/controllers/auth.dart | 256 ++++++-- lib/controllers/pickups_controller.dart | 445 +++++-------- lib/data/accepted_store.dart | 81 +++ lib/data/api_config.dart | 66 +- lib/data/geofence.dart | 578 +++++++++++++++++ lib/data/miler_api.dart | 178 +++++- lib/data/milk_run.dart | 61 +- lib/data/mock_backend.dart | 112 +++- lib/data/session.dart | 77 +++ lib/data/task_profile.dart | 446 +++++++++++++ lib/data/work_domain.dart | 4 +- lib/data/work_scope.dart | 63 +- lib/main.dart | 96 ++- lib/providers/auth/auth_provider.dart | 83 +-- .../pickuplog/pickuplog_provider.dart | 74 ++- lib/views/Dashboard/home/homepage.dart | 316 +++++++--- lib/views/Dashboard/home/stop_card.dart | 8 +- lib/views/Dashboard/home/trip_card.dart | 27 +- lib/views/Dashboard/pickups/card.dart | 113 +++- .../Dashboard/pickups/delivery_actions.dart | 170 +++++ lib/views/Dashboard/pickups/map.dart | 250 +++++++- lib/views/Dashboard/pickups/pickups.dart | 49 +- lib/views/Dashboard/pickups/sheet.dart | 2 +- lib/views/Dashboard/profile/Profilepage.dart | 29 +- lib/views/helpers/widgets/app_widgets.dart | 17 + lib/views/offline/offline_banner.dart | 26 +- lib/views/onboardscreens/Creat_mpin.dart | 32 +- lib/views/onboardscreens/Sign_in.dart | 54 +- test/activity_trips_test.dart | 16 +- test/arrived_needs_the_rider_there_test.dart | 103 +++ test/base_handover_card_test.dart | 233 +++++++ test/deliver_identity_test.dart | 23 +- test/delivery_leg_test.dart | 25 +- test/delivery_proof_test.dart | 10 +- test/geofence_flag_test.dart | 123 ++-- test/geofence_test.dart | 588 ++++++++++++++++++ test/hub_handover_action_test.dart | 249 ++++++++ test/logout_clears_session_test.dart | 185 ++++++ test/miler_contract_regression_test.dart | 26 +- test/mixed_work_policy_test.dart | 319 ++++++++++ test/multi_destination_stop_test.dart | 193 ++++++ test/multidrop_shot_test.dart | 170 +++++ test/offline_banner_test.dart | 25 +- test/parcel_photos_test.dart | 213 +++++++ test/set_pin_flow_test.dart | 386 ++++++++++++ test/shots_out/multidrop_queue.png | Bin 0 -> 231689 bytes test/shots_out/multidrop_single.png | Bin 0 -> 69560 bytes test/shots_out/multidrop_stop1.png | Bin 0 -> 79915 bytes test/shots_out/multidrop_stop2.png | Bin 0 -> 75887 bytes test/task_profile_test.dart | 253 ++++++++ test/work_scope_test.dart | 181 ++++-- 53 files changed, 6346 insertions(+), 748 deletions(-) delete mode 100644 android/key.properties create mode 100644 lib/data/geofence.dart create mode 100644 lib/data/session.dart create mode 100644 lib/data/task_profile.dart create mode 100644 test/arrived_needs_the_rider_there_test.dart create mode 100644 test/base_handover_card_test.dart create mode 100644 test/geofence_test.dart create mode 100644 test/hub_handover_action_test.dart create mode 100644 test/logout_clears_session_test.dart create mode 100644 test/mixed_work_policy_test.dart create mode 100644 test/multi_destination_stop_test.dart create mode 100644 test/multidrop_shot_test.dart create mode 100644 test/parcel_photos_test.dart create mode 100644 test/set_pin_flow_test.dart create mode 100644 test/shots_out/multidrop_queue.png create mode 100644 test/shots_out/multidrop_single.png create mode 100644 test/shots_out/multidrop_stop1.png create mode 100644 test/shots_out/multidrop_stop2.png create mode 100644 test/task_profile_test.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5d8bfec..86aa4f8 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,9 +4,17 @@ - - - + + + @@ -19,15 +27,43 @@ - - - - + + + - @@ -135,10 +171,6 @@ - - - - diff --git a/android/key.properties b/android/key.properties deleted file mode 100644 index 8a16a8a..0000000 --- a/android/key.properties +++ /dev/null @@ -1,4 +0,0 @@ -storePassword=W8UzA5NVaDTvix79rekmn8834m81tcKX -keyPassword=W8UzA5NVaDTvix79rekmn8834m81tcKX -keyAlias=doormile -storeFile=doormilerider-keystore.jks diff --git a/lib/controllers/auth.dart b/lib/controllers/auth.dart index 88f5f54..e84d858 100644 --- a/lib/controllers/auth.dart +++ b/lib/controllers/auth.dart @@ -10,9 +10,37 @@ import 'package:miler/utils/device.dart'; import 'package:miler/controllers/profile_controller.dart'; import 'package:miler/Models/login/login.dart'; import 'package:miler/data/api_config.dart'; +import 'package:miler/data/miler_api.dart'; import 'package:miler/views/helpers/widgets/miler_sheet_kit.dart'; -enum AuthNext { verifyPin, otp, notRegistered, error } +/// Which screen the phone number on the sign-in form has earned. +/// +/// ── `otp` is gone, and it was never real ── +/// +/// There is no OTP route on the miler side — `MilerApi` carries the whole auth +/// surface and it is login / set-pin / verify-pin / device-token. The old `otp` +/// branch fired when the directory said "no such account", sent the rider to a +/// code screen that verified nothing (`verifyOtp` returned `true` without +/// checking), and dead-ended at a Create-MPIN screen that could not write a +/// PIN. A rider who took it could not come back. +/// +/// The server answers this question directly now — see [MilerApi.pinSetOf]. +enum AuthNext { + /// `pin_set: true` — he has a PIN. Enter-PIN, exactly as before. + verifyPin, + + /// `pin_set: false` — a rider who has never signed in. Set-PIN. + setPin, + + /// 404. No miler account on this number. + notRegistered, + + /// 403. The row exists but is not an active miler. + inactive, + + /// The directory could not be reached. Not evidence about the rider. + error, +} class AuthController extends GetxController { final RxBool sendingOtp = false.obs; @@ -41,11 +69,18 @@ class AuthController extends GetxController { static const String _prefsUserEmailKey = 'user_email'; static const String _prefsContactNoKey = 'contactno'; static const String _prefsAddressKey = 'user_address'; - static const String _prefsForceMasterPinKey = 'force_master_pin'; - static const String _masterPinValue = '1234'; - static const String forceMasterPinPrefKey = _prefsForceMasterPinKey; - static const String masterPinValue = _masterPinValue; - bool _forceMasterPinFlow = false; + // ── The master-PIN constants are gone ── + // + // `_masterPinValue = '1234'`, `masterPinValue`, `forceMasterPinPrefKey` and + // `_forceMasterPinFlow` were declared here and read by nothing — the feature + // they belonged to was removed and its constants were not. A public constant + // named `masterPinValue` holding a four-digit PIN is an invitation to the + // next person looking for a shortcut, and it read as though the app still had + // a back door. Removed with the set-PIN work rather than left to be + // rediscovered. + // + // Riders set their own PIN now; `Creat_mpin.dart` refuses `1234` and `1111` + // along with every other trivial sequence. Future _notifyProfileController() async { try { if (Get.isRegistered()) { @@ -103,6 +138,26 @@ class AuthController extends GetxController { ); } + /// Which screen this phone number has earned, asked of the server. + /// + /// ── One call, one boolean, no guessing ── + /// + /// This used to ask `milerAccountExists`, which read *only the status code* + /// of `POST /miler/login` and threw the body away. From "an account exists" + /// it inferred Enter-PIN, and from "it does not" it inferred an OTP branch + /// that verified nothing and dead-ended at a screen which could not write a + /// PIN. A `null` — the directory unreachable — was read as "he has an + /// account", because the OTP direction was the worse place to be wrong. + /// + /// The server answers directly now. `pin_set` is the whole decision, and it + /// is read as a boolean rather than off the message beside it, which is prose + /// and will be reworded. + /// + /// The fallback when the field is absent — an older server, or a body that + /// did not parse — is **Enter-PIN**, for the same reason the old `null` case + /// chose it: a rider who does have a PIN can sign in, and one who does not + /// gets a refusal he can report. Sending him to Set-PIN on a guess earns a + /// 409 and a screen he cannot leave. Future precheckPhone(String phone) async { try { final normalized = _normalizePhone(phone); @@ -111,31 +166,34 @@ class AuthController extends GetxController { // The mocked "Demo Rider" (userid 9999) that used to be written here is // gone. It bypassed the server entirely and left a fake identity in prefs - // that outlived the session it was created for — every screen reading - // 'userid' got 9999 until the app was reinstalled. The real user is - // established by verify-pin and nowhere else. + // that outlived the session it was created for. The real user is + // established by verify-pin / set-pin and nowhere else. await prefs.setString(_prefsContactNoKey, normalized); - // On the live backend, ask whether this phone already belongs to an - // active miler account with a PIN on file. If it does, go straight to the - // MPIN screen: OTP delivery isn't live yet, and the OTP path ends at - // Create-MPIN, which would overwrite the PIN the account was issued. - // Seeded development accounts take exactly this branch — enter the phone, - // enter the seeded MPIN, done. - // Null means the directory could not be reached — see - // [AuthProvider.milerAccountExists]. Treat it as "he has an account", - // because that is true of every rider who gets this far and because the - // MPIN screen is the only one that can tell him what went wrong. The OTP - // branch is the dead end: it ends at Create-MPIN, which cannot write a - // PIN, so guessing wrong in that direction locks a rider out. - final exists = await _api.milerAccountExists(normalized); - if (exists ?? true) { - lastDecision = AuthNext.verifyPin; + final res = await MilerApi.login(normalized); + debugPrint( + '[AUTH][PRECHECK] $normalized -> ${res.status} ' + 'pin_set=${MilerApi.pinSetOf(res)} raw=${res.raw}', + ); + + if (res.status == 404) { + lastDecision = AuthNext.notRegistered; + return lastDecision!; + } + if (res.status == 403 || res.status == 401) { + lastDecision = AuthNext.inactive; + return lastDecision!; + } + if (!res.ok) { + // 5xx, a timeout, a body that did not parse. Not a fact about the + // rider, and not a reason to send him anywhere final. + lastDecision = AuthNext.error; return lastDecision!; } - // The directory answered, and said there is no such account. - lastDecision = AuthNext.otp; + lastDecision = MilerApi.pinSetOf(res) == false + ? AuthNext.setPin + : AuthNext.verifyPin; return lastDecision!; } catch (e) { debugPrint('Precheck phone error: $e'); @@ -144,6 +202,13 @@ class AuthController extends GetxController { } } + /// Why the last [setPin] failed, in the rider's words. Null on success. + String? lastSetPinFailure; + + /// True when [setPin] was refused because the account already has a PIN — + /// the caller sends the rider to Enter-PIN rather than showing an error. + bool lastSetPinWasAlreadySet = false; + Future sendOtp([String? phoneArg]) async { if (sendingOtp.value) return false; if (phoneArg != null && phoneArg.isNotEmpty) { @@ -179,52 +244,129 @@ class AuthController extends GetxController { return true; } + /// Creates this rider's PIN and signs him in. `POST /miler/set-pin`. + /// + /// ── What this replaces ── + /// + /// It called `AuthProvider.updatePin`, which had no route to call and + /// returned a manufactured `403 "Your MPIN is issued by your office and + /// cannot be changed from the app."` — correct while `reset-pin` was the only + /// PIN write and it needed an admin token, and a dead end for the rider + /// standing on the Create-MPIN screen. + /// + /// Riders set their own PIN on first sign-in now. The call returns a **full + /// session**, so this lands the rider logged in — there is no verify-pin + /// afterwards and no second screen. + /// + /// Returns true when the session is real. On a `409` — the account already + /// has a PIN — [lastSetPinWasAlreadySet] is set and the caller sends him to + /// Enter-PIN rather than showing him an error he cannot act on. Future setPin(String newPin) async { + lastSetPinFailure = null; + lastSetPinWasAlreadySet = false; + + final phone = currentPhone; + if (phone == null || phone.isEmpty) { + lastSetPinFailure = + 'We lost your number. Go back and enter it again.'; + return false; + } + if (newPin.length != 4 || int.tryParse(newPin) == null) { + lastSetPinFailure = 'Enter a 4-digit PIN.'; + return false; + } + try { final prefs = await SharedPreferences.getInstance(); - int? userId = - prefs.getInt(_prefsPendingPinUserIdKey) ?? - prefs.getInt(_prefsUserIdKey); - if (newPin.length != 4 || int.tryParse(newPin) == null) { - _showBottomSheet( - title: 'Invalid PIN', - message: 'Please enter a valid 4-digit PIN.', - ); + String deviceId = ''; + String fcmToken = ''; + try { + deviceId = await DeviceUtils.ensureDeviceId(prefs); + } catch (e) { + debugPrint('[AUTH] device id unavailable, continuing: $e'); + } + try { + fcmToken = await DeviceUtils.ensureFcmToken(prefs); + } catch (e) { + debugPrint('[AUTH] fcm token unavailable, continuing: $e'); + } + + // Same rule as verify-pin: only THIS attempt may grant a session, so a + // stale token cannot make a refused set-pin look accepted. + await ApiConfig.clearToken(); + + final Login res = await _api.loginParsed( + contactNo: phone, + deviceType: Platform.operatingSystem, + configId: 6, + deviceId: deviceId, + fcmToken: fcmToken, + pinRaw: newPin, + firstTime: true, + ); + + // `_loginNew` normalises the envelope as `code: ok ? 200 : httpStatus`, + // so on a refusal this IS the server's status line. `httpstatus` is on + // the envelope too but `Login` does not parse it. + final int http = res.code ?? 0; + + // ── 409 is not a failure the rider can fix by trying again ── + // + // It means the account already has a PIN — he is not a first-time rider + // after all, or he set one on another handset. The caller sends him to + // Enter-PIN; telling him "could not save your PIN" would leave him + // retyping a PIN the server will never accept. + if (http == 409) { + lastSetPinWasAlreadySet = true; + lastSetPinFailure = + 'You already have a PIN on this number. Enter it to sign in.'; return false; } - if (userId == null) { - _showBottomSheet( - title: 'Error', - message: 'User ID not found. Please try again.', - ); + if (http == 404) { + lastSetPinFailure = + 'That number is not registered as a Miler. Contact your manager.'; + return false; + } + if (http == 403 || http == 401) { + lastSetPinFailure = + 'This account is not active. Contact your manager.'; return false; } - final int pinNum = int.parse(newPin); - final res = await _api.updatePin(userId: userId, pin: pinNum); - if (res.statusCode >= 200 && res.statusCode < 300) { + final bool serverAccepted = res.status == true; + final String? token = await ApiConfig.getToken(); + final bool haveSession = token != null && token.isNotEmpty; + + if (serverAccepted && !haveSession) { + // The PIN was created and there is nothing to sign in with. An + // integration fault, and it must never be reported as the rider's + // mistake — see the same branch in [verifyPinWithServer]. + debugPrint('[AUTH] set-pin succeeded but returned no usable token'); + lastSetPinFailure = + 'Your PIN was saved, but the server did not return a session. ' + 'Sign in with your new PIN.'; + lastSetPinWasAlreadySet = true; + return false; + } + + if (serverAccepted && haveSession) { await prefs.setString('dbPin', newPin); + await prefs.setBool('logged_out', false); + await prefs.setString(_prefsContactNoKey, phone); await prefs.remove(_prefsPendingPinUserIdKey); + await _notifyProfileController(); return true; } - // The server's own sentence, not a slice of its JSON. A rider reading - // `{"status":false,"code":403,...}` learns nothing he can act on. - String reason = ''; - try { - final decoded = json.decode(res.body); - if (decoded is Map) reason = (decoded['message'] ?? '').toString(); - } catch (_) {} - if (reason.trim().isEmpty) { - reason = 'Could not set your MPIN. Please contact your manager.'; - } - _showBottomSheet(title: 'MPIN not changed', message: reason); + + final String serverMsg = (res.message ?? '').trim(); + lastSetPinFailure = serverMsg.isNotEmpty && serverMsg.length < 140 + ? serverMsg + : 'Could not set your PIN. Check your connection and try again.'; return false; } catch (e) { debugPrint('setPin error: $e'); - _showBottomSheet( - title: 'Error', - message: 'Something went wrong while setting the PIN.', - ); + lastSetPinFailure = + 'Something went wrong while setting your PIN. Try again.'; return false; } } diff --git a/lib/controllers/pickups_controller.dart b/lib/controllers/pickups_controller.dart index 06d8922..e9776e5 100644 --- a/lib/controllers/pickups_controller.dart +++ b/lib/controllers/pickups_controller.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import 'package:miler/data/geofence.dart'; import 'package:miler/data/work_repository.dart'; import 'package:flutter/material.dart'; import 'package:latlong2/latlong.dart' show LatLng; @@ -19,133 +20,38 @@ import 'package:minio/io.dart'; import 'package:miler/utils/kalman_filter.dart'; import 'package:miler/utils/mqtt_service.dart'; import 'package:miler/controllers/connectivity_mixin.dart'; -import 'package:miler/views/helpers/constants/Colorconstants.dart'; import 'package:miler/data/meal_run_mock.dart'; import 'package:miler/views/helpers/widgets/app_widgets.dart'; -/// ── PROXIMITY ENFORCEMENT IS OFF ── +/// ── PROXIMITY ENFORCEMENT IS ON ── /// -/// Off again on 2026-08-25, the same day it was turned on, at the founder's -/// call. Nothing was found wrong with it — this is a decision about when to -/// switch it on, not a retraction of the work. +/// The fence itself moved to `lib/data/geofence.dart` on 2026-09-16. What used +/// to live here — four constants, a `_freshFix` ladder and a 130-line +/// `_checkGeofence` — could only be reached from a `GetxController`, so Home's +/// bulk gate grew a second copy of the arithmetic with a different radius, and +/// none of it could be tested without a real handset. /// -/// It is off for **every** status — arrived, picked, picked up, delivery -/// arrived, delivered, cancelled — and on both gates, this one and the bulk -/// check on Home. Half a fence is worse than none: it would block one route -/// into a rung and wave through another, with two different messages and no -/// explanation of why one worked. +/// [Geofence] is that logic, extracted and corrected. Four things changed with +/// it, and each one was a way a rider could mark a stop he was not standing at: /// -/// ── What it costs while it is off ── +/// * **It is on.** `kGeofenceEnforced` defaulted to `false`, so +/// `_checkGeofence` returned `true` on its third line in every shipped +/// build. +/// * **The radius is 100 m, not 10.** Ten metres is inside consumer-GPS error, +/// which is what got the fence switched off twice before. +/// * **It fails closed.** The old `catch` returned `true` with the comment +/// "allow on error (fail-safe)". Any throw from the location stack opened +/// every rung in the app. +/// * **The phone's error is no longer credited to the rider.** The comparison +/// was `distance - accuracy > radius`, so slack grew with inaccuracy and a +/// ±250 m fix bought 250 m of it. A fix too vague to resolve the fence is +/// refused instead — see `kGeofenceMaxAccuracyMetres`. /// -/// This is the one control that decides whether the app is telling the truth -/// about where a rider was standing when he said a stop was done. With it off, -/// "Picked up" means he pressed a button, not that he was there — the -/// timestamps and the GPS still ride along on every status write, so the office -/// can audit after the fact, but nothing is refused at the moment of the press. -/// -/// ── What is ready for the day it goes back on ── -/// -/// Recorded here because the work is done and the flag is the only thing -/// holding it, so nobody has to rediscover any of it. The fence was off from -/// 2026-08-19 because it refused real work — riders pressing **Picked up** at a -/// counter were told "You're 4.2 km from this stop" — and that was a -/// measurement problem rather than a strictness one. Three causes, all fixed: -/// -/// • **The fix was not worth measuring with.** Home handed the fence a -/// `LocationAccuracy.low` position — a ~1 km hint on Android — or a cached -/// one of any age. See [_freshFix] and [kGeofenceFixMaxAge]. -/// • **The phone's own error was charged to the rider.** The comparison is -/// `distance - accuracy > radius`, so a rider 12 m out on a ±20 m fix is not -/// refused for a precision the hardware never provided. -/// • **There were three radii.** A configured `pickupradius` defaulting to -/// 100 here, a hardcoded 500 in Home's bulk gate, and no agreement between -/// them. There is one now: [kGeofenceRadiusMeters], set to 10. -/// -/// All three are live in the code below and simply do not run while this is -/// false. The fourth cause is real and none of this fixes it: **the booking's -/// own coordinates are often wrong**, because they come from wherever the -/// customer dropped a pin. A stop carrying *no* coordinates is allowed through -/// — that is the hub's data, not something a rider can resolve from a doorstep -/// — but a stop carrying wrong ones will still refuse him. -/// -/// ── Turning it on ── -/// -/// flutter run --dart-define=ENFORCE_GEOFENCE=true -/// flutter build apk --dart-define=ENFORCE_GEOFENCE=true -/// -/// Or flip the default. Ten metres is tight — at or inside consumer GPS -/// accuracy — so if the first reports are riders blocked at doors, raise -/// [kGeofenceRadiusMeters] before reaching for this switch again. -/// -/// Every bypass is logged in every build mode (see `_checkGeofence`), so a -/// build's own log says which way it was compiled. -/// -/// ── The history, so none of the old mistakes come back ── -/// -/// This was once `kDebugMode`, which meant the fence was off in exactly the -/// builds anyone tested with — so it was never really tested. It was then -/// pinned to a hard `false`, which left no way to walk the flow at a desk. The -/// shape below survives both: one named constant, one define, one default, and -/// the default is a product decision rather than a side effect of how the app -/// was built. -const bool kGeofenceEnforced = bool.fromEnvironment( - 'ENFORCE_GEOFENCE', - defaultValue: false, -); - -/// The name every call site reads. Derived, so there is one switch and not two. -const bool kBypassGeofenceForTesting = !kGeofenceEnforced; - -/// ───────────────────────────────────────────────────────────────────────── -/// HOW CLOSE IS "AT THE STOP" — 10 metres -/// -/// A product decision, taken deliberately and tight: the rider must be *at the -/// door*, not on the street outside it, before he can mark a stop arrived, -/// picked or delivered. -/// -/// ── Why this is a constant and no longer the server's `pickupradius` ── -/// -/// The fence used to read `pickupradius` out of prefs, which login writes from -/// the profile and defaults to 100. That made the strictness of the app's one -/// honesty control a per-tenant configuration value nobody on this side could -/// see, and it silently disagreed with the second gate on Home, which was -/// hardcoded to 500. Three numbers for one rule. This is the rule. -/// -/// ── What 10 metres actually demands, stated plainly ── -/// -/// This is at or inside the accuracy of consumer GPS. A phone reports a fix -/// with an error radius, and 5–15 m in the open is normal while 30–50 m -/// between buildings is ordinary rather than exceptional. A fence smaller than -/// the error it is measured with will refuse a rider who is genuinely standing -/// at the door — which is the exact failure that got this whole control turned -/// off once before, and the reason two things below are not optional: -/// -/// • **The fix must be worth 10 m.** [LocationAccuracy.best], not the `low` -/// the callers were passing — `low` is a ~1 km hint on Android and against -/// a 10 m fence it is not a measurement, it is a coin toss. See -/// [_freshFix]. -/// -/// • **The phone's own error is credited to the rider.** The check is -/// `distance - accuracy > radius`, not `distance > radius`: a rider 12 m -/// away on a fix that says ±20 m has not been shown to be outside the -/// fence, and refusing him is asserting a precision the hardware did not -/// provide. He is refused when the *phone* says he is outside, not when the -/// arithmetic does. -/// -/// Together those keep 10 m meaning "at the door" without it meaning "when the -/// satellites are kind". If riders still report being blocked at a door, this -/// number is the knob — raise it here, in one place, rather than turning the -/// fence off again. -const double kGeofenceRadiusMeters = 10; - -/// How stale a cached fix may be before the fence refuses to measure with it. -/// -/// A last-known position is instant and free and can be an hour old. Against a -/// 100 m fence that was survivable; against 10 m it is how a rider marks a -/// delivery arrived from the previous street because that is where the phone -/// last looked. -const Duration kGeofenceFixMaxAge = Duration(seconds: 30); - +/// What is left in this file is the wiring: [_checkGeofence] adapts [Geofence] +/// to the eight call sites below, and every one of them returns **before** +/// building a payload when it answers false. That is asserted end-to-end in +/// `geofence_test.dart` with an HTTP client that fails the test if any request +/// is sent. class PickupsController extends GetxController with ConnectivityControllerMixin { MilerKalmanFilter? _kf; @@ -332,12 +238,12 @@ class PickupsController extends GetxController // ---------------- Location helpers ---------------- /// The error radius, in metres, of the fix [_ensureLatLng] last obtained. /// - /// Read by [_checkGeofence], which credits it to the rider — see - /// [kGeofenceRadiusMeters]. Starts at zero so a fence measured before any fix - /// has been taken is strict rather than accidentally generous. + /// Telemetry only. The fence no longer reads this — [Geofence] takes its own + /// fix and rejects one it cannot trust rather than crediting its error to the + /// rider. Kept because the payload builders log it. double _lastFixAccuracy = 0; - /// A position good enough to measure a [kGeofenceRadiusMeters] fence with. + /// A position good enough to put on a status payload. /// /// ── What this replaced, and why it had to go ── /// @@ -380,7 +286,7 @@ class PickupsController extends GetxController } else if (cached != null) { debugPrint( '[GEOFENCE] cached fix is ${age?.inSeconds}s old — too stale to ' - 'measure a ${kGeofenceRadiusMeters.toStringAsFixed(0)}m fence', + 'measure a ${kGeofenceRadiusMetres.toStringAsFixed(0)}m fence', ); } } catch (_) {} @@ -398,34 +304,28 @@ class PickupsController extends GetxController final needsFetch = (lat == '0' || lat.isEmpty || lng == '0' || lng.isEmpty); - // ── The two shortcuts below are disabled while the fence is on ── + // ── These shortcuts are safe again, because nothing is decided on them ── // - // Every caller of this method feeds its answer to [_checkGeofence] and - // then puts the same pair on the payload. Both shortcuts hand back a - // position of unknown provenance: the first trusts whatever the screen - // passed in — Home passes a `LocationAccuracy.low` fix, which is a ~1 km - // hint — and the second reuses a cached value with no age on it at all. - // Neither carries an accuracy, so [_lastFixAccuracy] would be stale too - // and the fence would measure a 10 m rule with a number it cannot - // characterise. + // They used to be gated on the fence being off, and rightly: the fence + // measured with whatever this returned, and both shortcuts hand back a + // position of unknown provenance — the first trusts whatever the screen + // passed in (Home passes a `LocationAccuracy.low` fix, a ~1 km hint on + // Android), the second reuses a cached value with no age on it at all. // - // With the fence enforced this takes one real fix per status write. That - // is a few seconds, once, at a door the rider is standing still at — and - // it is the whole basis on which the app is about to refuse or allow his - // press. With the fence off the shortcuts stand: nothing is being decided - // on the answer, it is telemetry. - if (kBypassGeofenceForTesting) { - // Fast path: if we have valid coordinates, use them immediately - if (!needsFetch) return {'lat': outLat, 'lng': outLng}; + // [Geofence] takes its own `best` fix now and characterises it before + // deciding, so this method is back to what its name says: the coordinates + // that go **on the payload**. Telemetry, not evidence. Taking a second + // 8-second fix for it would cost the rider time at every door to improve + // a number nobody adjudicates. + // Fast path: if we have valid coordinates, use them immediately + if (!needsFetch) return {'lat': outLat, 'lng': outLng}; - // Reuse recently cached coordinates first if fresh (e.g. within 30s) - // For now just check if they exist to save time - if (currentLat.value.isNotEmpty && - currentLat.value != '0' && - currentLng.value.isNotEmpty && - currentLng.value != '0') { - return {'lat': currentLat.value, 'lng': currentLng.value}; - } + // Reuse recently cached coordinates first if fresh (e.g. within 30s) + if (currentLat.value.isNotEmpty && + currentLat.value != '0' && + currentLng.value.isNotEmpty && + currentLng.value != '0') { + return {'lat': currentLat.value, 'lng': currentLng.value}; } final serviceEnabled = await Geolocator.isLocationServiceEnabled(); @@ -1212,12 +1112,15 @@ class PickupsController extends GetxController // ---------------- Geofencing helpers ---------------- // - // The radius is [kGeofenceRadiusMeters] and nothing else. It read the - // server's `pickupradius` out of prefs, which meant the app's one honesty - // control was a per-tenant number nobody here could see — and it disagreed - // with Home's own hardcoded 500. `pickupradius` is still stored at login; - // it simply no longer decides this. - double get _geofenceRadius => kGeofenceRadiusMeters; + // The radius is [kGeofenceRadiusMetres], in `lib/data/geofence.dart`, and + // nothing else. It read the server's `pickupradius` out of prefs, which made + // the app's one honesty control a per-tenant number nobody here could see — + // and it disagreed with Home's own hardcoded 500. `pickupradius` is still + // stored at login; it simply no longer decides this. + + /// The fence's answer to the last rung that asked, for screens that want to + /// show the live distance rather than only the refusal. + GeofenceDecision? lastGeofence; // Helper method to show snackbar reliably in both debug and release builds // @@ -1304,6 +1207,18 @@ class PickupsController extends GetxController /// a stop the hub keeps rejecting. See [updatePickedStatus]. String? lastPickupRefusal; + /// The proximity gate for one rung. **False means do not call the API.** + /// + /// Adapts [Geofence] to the call sites below. The rider's own coordinates are + /// no longer taken as arguments: the fence takes its *own* fix, at + /// [LocationAccuracy.best], because the pair the screens pass in comes from a + /// `low`-accuracy Home poll or an unaged cache and carries no accuracy at all + /// — so the fence could not tell how much to trust the number it was + /// measuring a 100 m rule with. The parameters stay for the eight call sites + /// that build a payload from the same values; they are ignored here. + /// + /// Sets [lastBlockedReason] to the rider-facing sentence on every refusal, so + /// a caller can say what happened instead of reporting a network failure. Future _checkGeofence( double targetLat, double targetLng, @@ -1313,132 +1228,20 @@ class PickupsController extends GetxController ) async { lastBlockedReason = null; - // Opt-in via `--dart-define=BYPASS_GEOFENCE=true`; enforced otherwise. See - // the declaration for why it is a define rather than a constant. - // - // Logged with `debugPrint` in *every* build mode, deliberately — not behind - // `kDebugMode` like the diagnostics below. The whole risk of this flag is a - // build going out with proximity silently off, so the one thing it must - // never be is quiet. - if (kBypassGeofenceForTesting) { - debugPrint( - '[GEOFENCE] OFF for $action — enforcement is disabled in this build. ' - 'Stop completion is NOT proximity-verified. ' - 'See kGeofenceEnforced.', - ); - return true; - } - - // Validate coordinates - ensure they are valid GPS coordinates - final bool hasValidTarget = - targetLat != 0 && - targetLng != 0 && - targetLat.abs() <= 90 && - targetLng.abs() <= 180; - final bool hasValidCurrent = - currentLat != 0 && - currentLng != 0 && - currentLat.abs() <= 90 && - currentLng.abs() <= 180; - - // ── Two ways to have no coordinates, and only one of them is the rider's ── - // - // This used to treat both the same and wave both through: "Missing - // coordinates. Proceeding with update." That is the bypass that makes a - // fence decorative — turn location off and every rung opens — and it was - // survivable only because the fence itself was off. - // - // **No target.** The booking carries no pin. That is the hub's data, the - // rider cannot fix it from a doorstep, and blocking him leaves the stop - // unworkable by anyone. Allowed, and logged, exactly as before. - if (!hasValidTarget) { - debugPrint( - '[GEOFENCE] $action allowed: the stop carries no coordinates ' - '($targetLat, $targetLng), so proximity cannot be checked. This is a ' - 'data gap on the booking, not a rider who is somewhere else.', - ); - return true; - } - - // **No fix.** Location is off, permission is denied, or the GPS did not - // settle in time. This one the rider *can* fix, and it is the difference - // between a fence and a suggestion — so it is refused, and the message - // says which of the three to go and change. - if (!hasValidCurrent) { - debugPrint( - '[GEOFENCE] $action refused: no usable fix ' - '($currentLat, $currentLng)', - ); - lastBlockedReason = - 'Your phone could not find your location, so this stop cannot be ' - 'marked ${action.toLowerCase()}. Turn location on, allow it for ' - 'Miler, and step outside if you can.'; - _showErrorSnackbar('Location Error', lastBlockedReason!, seconds: 5); - return false; - } - - try { - final radius = _geofenceRadius; - final distance = Geolocator.distanceBetween( - targetLat, - targetLng, - currentLat, - currentLng, - ); - final distanceKm = distance / 1000.0; - final distanceMeters = distance; - - // ── The phone's own error is credited to the rider ── - // - // A fix carries an accuracy in metres, and at a 10 m fence that number - // is the same size as the thing being measured. Comparing a raw distance - // against 10 m asserts a precision the hardware did not provide, and the - // rider standing at the door on a ±25 m fix is the one it refuses. - // - // So the fence is measured against the *nearest point the phone allows*: - // 12 m away on a ±20 m fix has not been shown to be outside it. He is - // blocked when the phone says he is outside, not when the arithmetic - // does. See [kGeofenceRadiusMeters]. - final slack = _lastFixAccuracy; - final effective = (distance - slack).clamp(0.0, double.infinity); - - debugPrint( - '[GEOFENCE] $action | target ($targetLat, $targetLng) ' - '| rider ($currentLat, $currentLng) ' - '| ${distanceMeters.toStringAsFixed(1)}m ±${slack.toStringAsFixed(0)}m ' - '→ ${effective.toStringAsFixed(1)}m vs ${radius.toStringAsFixed(0)}m', - ); - - if (effective > radius) { - // ── One sentence, in metres he can act on ── - // - // Was three lines of "Distance: 4213 m (4.21 km) / Required: Within - // 100 m" — a readout, in a snackbar, on a phone in a jacket pocket. - // What the rider needs is how far he still has to go. - final String away = distanceMeters >= 1000 - ? '${distanceKm.toStringAsFixed(1)} km' - : '${distanceMeters.toStringAsFixed(0)} m'; - lastBlockedReason = - "You're $away from this stop — get within " - '${radius.toStringAsFixed(0)} m to mark it ' - '${action.toLowerCase()}'; - _showErrorSnackbar('Location Error', lastBlockedReason!, seconds: 5); - return false; - } - return true; - } catch (e) { - if (kDebugMode) { - debugPrint('[GEOFENCE] Error calculating distance: $e'); - } - // On error, show warning but allow (fail-safe) - _showErrorSnackbar( - 'Location Warning', - 'Unable to verify distance. Proceeding with caution.', - bgColor: ColorConstants.warning, - seconds: 3, - ); - return true; // Allow on error (fail-safe) + final decision = await Geofence.check( + targetLat: targetLat, + targetLng: targetLng, + action: action, + ); + + lastGeofence = decision; + if (decision.allowed) return true; + + lastBlockedReason = decision.reason; + if (decision.reason != null) { + _showErrorSnackbar('Location Error', decision.reason!, seconds: 5); } + return false; } // Get last pickup location (for calculating riderkms between pickup) @@ -1824,6 +1627,14 @@ class PickupsController extends GetxController final mock = _mockStatus(pickupId, 'arrived'); if (mock != null) return mock; + // Cleared on entry, not only on the paths that set them. The caller now + // reads these to decide whether a failed arrival may still advance the rung + // locally, and a value left over from an earlier stop would answer for this + // one — refusing an arrival because a different door refused twenty minutes + // ago. `lastBlockedReason` gets the same treatment inside `_checkGeofence`. + lastArrivalRefusal = null; + lastArrivalNotice = null; + // START LOADING IMMEDIATELY for better UX arrivedShimmer.value = true; try { @@ -1878,6 +1689,20 @@ class PickupsController extends GetxController ? resp!['message'].toString() : 'Your office would not accept this arrival.') : null; + // ── A write that never landed must not look like one that did ── + // + // `lastArrivalRefusal` stays null on a transport failure, deliberately: + // the caller lets a rider with no signal carry on rather than stranding + // him at a door. But it then advanced the rung *silently*, so the rider + // saw ARRIVED and had no way to know the hub had not been told — and + // the first person to find out was an operator wondering why he had + // been at a kitchen for forty minutes. + // + // He still carries on. He is simply told. + lastArrivalNotice = lastArrivalRefusal == null + ? 'Marked arrived on your phone. Your office has not been told — ' + 'you had no connection. Tell them if it matters.' + : null; debugPrint('[UPDATE][ARRIVED][FAILED] resp=${jsonEncode(resp)}'); return false; } @@ -1957,18 +1782,25 @@ class PickupsController extends GetxController final dLat = double.tryParse(dropLat) ?? 0.0; final dLng = double.tryParse(dropLng) ?? 0.0; + // ── Unconditional ── + // + // This was wrapped in `if (dLat != 0 && dLng != 0)`, so a consignment + // whose drop pin was missing skipped the fence entirely — silently, with + // no log and no record. That is the one case where the fence matters + // most: nobody can say afterwards where the rider was standing. A missing + // pin is now [GeofenceOutcome.noTarget] and is refused with a sentence + // that sends the rider to his office. + // // Fenced against the DROP, not the pickup. Using the pickup coordinates - // here would fence the rider to the kitchen he left an hour ago. - if (dLat != 0 && dLng != 0) { - final inFence = await _checkGeofence( - dLat, - dLng, - rLat, - rLng, - 'Delivery arrived', - ); - if (!inFence) return false; - } + // here would fence the rider to the counter he left an hour ago. + final inFence = await _checkGeofence( + dLat, + dLng, + rLat, + rLng, + 'Delivery arrived', + ); + if (!inFence) return false; // The clock the completion record reads, same key the pickup leg uses. final prefs = await SharedPreferences.getInstance(); @@ -2027,16 +1859,25 @@ class PickupsController extends GetxController final dLat = double.tryParse(dropLat) ?? 0.0; final dLng = double.tryParse(dropLng) ?? 0.0; - if (dLat != 0 && dLng != 0) { - final inFence = await _checkGeofence( - dLat, - dLng, - rLat, - rLng, - 'Delivered', - ); - if (!inFence) return false; - } + // ── Unconditional ── + // + // This was wrapped in `if (dLat != 0 && dLng != 0)`, so a consignment + // whose drop pin was missing skipped the fence entirely — silently, with + // no log and no record. That is the one case where the fence matters + // most: nobody can say afterwards where the rider was standing. A missing + // pin is now [GeofenceOutcome.noTarget] and is refused with a sentence + // that sends the rider to his office. + // + // Fenced against the DROP, not the pickup. Using the pickup coordinates + // here would fence the rider to the counter he left an hour ago. + final inFence = await _checkGeofence( + dLat, + dLng, + rLat, + rLng, + 'Delivered', + ); + if (!inFence) return false; _pickedTime = _formatDateTimeFull(DateTime.now()); diff --git a/lib/data/accepted_store.dart b/lib/data/accepted_store.dart index 9371ca7..bff9b7e 100644 --- a/lib/data/accepted_store.dart +++ b/lib/data/accepted_store.dart @@ -57,6 +57,84 @@ Future _drainLegacy(String base, WorkScope scope) async { debugPrint('[SCOPE] drained legacy "$base" into ${scope.key}'); } +/// Moves records out of the old line-suffixed keys into this scope's key. +/// +/// ── Why this one MERGES where [_drainLegacy] drops ── +/// +/// A line-suffixed key is not anonymous the way a global one is. It already +/// names the rider and the tenant — `completed_bookings::u38.t13.milkMan` — +/// so everything in it provably belongs to this scope. There is nothing to +/// attribute and nothing to guess, and dropping it would throw away work the +/// rider actually did. +/// +/// Both old drawers are merged, because the whole point of removing the line +/// is that one rider's day is one day: a meal round and a logistics collection +/// finished in the same shift belong in the same history. Ids already present +/// win, so a re-run cannot duplicate a row. +Future _drainLineScoped(String base, WorkScope scope) async { + final prefs = await SharedPreferences.getInstance(); + final target = scope.scoped(base); + + // Read the target through `get`, not the typed accessors: these base keys + // hold a JSON blob for record stores and a String list for id stores, and + // `getStringList` throws outright when it meets the blob. + final Object? existing = prefs.get(target); + final merged = >[]; + final mergedIds = {}; + final ids = {}; + + if (existing is String) { + for (final row in _decode(existing)) { + merged.add(row); + final id = _rowKey(row); + if (id.isNotEmpty) mergedIds.add(id); + } + } else if (existing is List) { + ids.addAll(existing.map((e) => e.toString())); + } + + var moved = false; + for (final legacy in scope.legacyScopedKeys(base)) { + if (legacy == target || !prefs.containsKey(legacy)) continue; + final Object? value = prefs.get(legacy); + + if (value is String) { + for (final row in _decode(value)) { + final id = _rowKey(row); + if (id.isNotEmpty && !mergedIds.add(id)) continue; + merged.add(scope.stamp(row)); + } + } else if (value is List) { + ids.addAll(value.map((e) => e.toString())); + } + await prefs.remove(legacy); + moved = true; + } + if (!moved) return; + + if (merged.isNotEmpty) { + await prefs.setString(target, jsonEncode(merged)); + } else if (ids.isNotEmpty) { + await prefs.setStringList(target, ids.toList()); + } + debugPrint('[SCOPE] merged line-scoped "$base" into ${scope.key}'); +} + +/// The identity a stored row is de-duplicated on while merging. +String _rowKey(Map row) { + for (final k in const [ + 'orderid', + 'OrderId', + 'bookingid', + 'consignmentid', + 'id', + ]) { + final v = row[k]; + if (v != null && v.toString().trim().isNotEmpty) return v.toString(); + } + return ''; +} + /// Runs the one-time drain for every legacy key. Cheap after the first call — /// `containsKey` on a loaded prefs map. Future migrateLegacyStores() async { @@ -75,6 +153,9 @@ Future migrateLegacyStores() async { _kOrderLabelsKeyBase, ]) { await _drainLegacy(base, scope); + // Then fold in anything the old line-suffixed keys still hold. Order + // matters: the global drain writes the scoped key, and this merges into it. + await _drainLineScoped(base, scope); } } diff --git a/lib/data/api_config.dart b/lib/data/api_config.dart index ccce2b4..ed83baa 100644 --- a/lib/data/api_config.dart +++ b/lib/data/api_config.dart @@ -295,6 +295,44 @@ class ApiConfig { 'referenceno', 'orderid', ]); + + // ── One customer pickup can be several drops ── + // + // A customer-app booking carries N destinations and `GET /miler/bookings` + // returns ONE ROW PER DESTINATION once the pickup has been collected — + // same `bookingid`, same `bookingreference`, different door. The backend + // labels them for us: `destinationseq` is which one this is and + // `destinationcount` how many there are (both absent, or 1, on every + // console and milk-run booking, which is the whole existing world). + // + // Everything the rider's device remembers about a stop is keyed on + // `orderid` — the accepted store dedupes on it, the consignment-id map + // files under it, the collected and out-for-delivery sets hold it, the ETA + // and km keys are built from it. So three drops sharing one `orderid` is + // not a display bug: `addAcceptedBookings` deduped two of the three away + // before any screen saw them, and the two consignment ids that lost the + // race were unrecoverable, which is a bag the rider is holding with no + // door to take it to. + // + // So `orderid` becomes the **stop** key and carries the destination on it. + // The booking's own reference is untouched below in `bookingreference`, + // which is what every screen shows the rider and what he reads out on the + // phone. Single-destination bookings are byte-identical to before — the + // suffix only exists where there is something to tell apart. + final destinationCount = + int.tryParse( + (pick(['destinationcount', 'destinationCount']) ?? '').toString(), + ) ?? + 1; + final destinationSeq = + int.tryParse( + (pick(['destinationseq', 'destinationSeq']) ?? '').toString(), + ) ?? + 0; + final bookingKey = ref ?? id; + final stopKey = destinationCount > 1 + ? '$bookingKey#$destinationSeq' + : bookingKey; final status = pick([ 'status', 'bookingstatus', @@ -320,11 +358,37 @@ class ApiConfig { return { // identity 'pickupid': id, - 'orderid': ref ?? id, + // The STOP key — see the note above. `bookingreference` is the booking's + // own name and is what gets shown; this is what gets remembered. + 'orderid': stopKey, 'orderheaderid': id, 'bookingid': id, // keep original too 'bookingreference': s(ref), + // ── Which drop of the visit this is ── + // + // Carried so a card can say "Stop 2 of 3" rather than showing three rows + // that are identical down to the reference number. `destinationcount` is + // 1 for every single-destination and console booking, and the UI treats + // 1 as "say nothing". + 'destinationseq': destinationSeq, + 'destinationcount': destinationCount, + + // ── The parcel's own number, and who is waiting for it ── + // + // All three exist per DESTINATION, not per booking: on a three-drop + // pickup each door has its own tracking number and its own receiver, and + // the booking-level customer is the SENDER, who is not at any of them. + // Dropped by this adapter until now, so the rider arrived at a stranger's + // door with the sender's name on his screen and no number to read out. + 'trackingno': s(pick(['trackingno', 'trackingNo', 'tracking_no'])), + 'recipientname': s( + pick(['recipientname', 'recipientName', 'recipient_name']), + ), + 'recipientphone': s( + pick(['recipientphone', 'recipientPhone', 'recipient_phone']), + ), + // status 'orderstatus': fromConsignment.isNotEmpty ? fromConsignment diff --git a/lib/data/geofence.dart b/lib/data/geofence.dart new file mode 100644 index 0000000..15837de --- /dev/null +++ b/lib/data/geofence.dart @@ -0,0 +1,578 @@ +/// ───────────────────────────────────────────────────────────────────────── +/// THE PROXIMITY FENCE — one radius, one policy, one answer +/// +/// A rider may not tell the hub he is at a door he is not at. Every +/// location-sensitive rung — arrived, picked, picked up, delivery arrived, +/// delivered, handed over — is measured here and nowhere else. +/// +/// ── Why this is its own file ── +/// +/// The measurement used to live inside `PickupsController`, which cost three +/// things: +/// +/// * it could only be reached from a `GetxController`, so Home's bulk gate +/// grew a second copy with a different radius (500 m against the +/// controller's 100, later 10); +/// * it could not be tested without a real `Geolocator`, so the arithmetic was +/// never exercised at the values that decide a rider's day — 99 m and 101 m; +/// * it returned a bare `bool`, so a screen could not tell "he is 240 m away" +/// from "his GPS is off", and showed one sentence for both. +/// +/// This returns a [GeofenceDecision] carrying `allowed`, `distanceMetres`, +/// `reason`, `accuracyMetres` and `timestamp`, and takes its position through +/// an injectable seam ([positionProvider]) so the whole table is a unit test +/// rather than a walk around a car park. +/// +/// ── THE RULE ── +/// +/// distance > kGeofenceRadiusMetres → BLOCKED, and no API call is made. +/// +/// Blocked means blocked. A caller that receives `allowed == false` must return +/// before it builds a payload — not warn and continue. `geofence_test.dart` +/// asserts that with a `MockClient` that fails the test if any request is sent. +/// +/// ── Fail CLOSED ── +/// +/// Every failure to *measure* is a refusal: no permission, no fix, a stale one, +/// a vague one, a throw from the platform channel, and a booking carrying no +/// coordinates. The previous implementation allowed on four of those six, which +/// made the fence decorative — switching location off opened every rung in the +/// app. +/// ───────────────────────────────────────────────────────────────────────── +library; + +import 'package:flutter/foundation.dart'; +import 'package:geolocator/geolocator.dart'; + +/// The one switch, and it is **ON**. +/// +/// ── The default changed on 2026-09-16, and it is not to change back ── +/// +/// This was `defaultValue: false`, so every release build shipped with the +/// fence off and `_checkGeofence` returned `true` on its third line. "Picked +/// up" meant the rider pressed a button, not that he was there. +/// +/// The define survives as an **ops kill switch**, not a development +/// convenience: if the fence starts refusing real work at real doors, a build +/// with `--dart-define=ENFORCE_GEOFENCE=false` gets the fleet moving again +/// within one release rather than one sprint. Every bypassed check logs, in +/// every build mode, so a build's own log says which way it was compiled. +/// +/// Raise [kGeofenceRadiusMetres] before reaching for this. +const bool kGeofenceEnforced = bool.fromEnvironment( + 'ENFORCE_GEOFENCE', + defaultValue: true, +); + +/// How close "at the stop" is, in metres. +/// +/// ── Why 100 and not 10 ── +/// +/// This was 10 m for one day. Ten metres is at or inside the error radius of +/// consumer GPS: a phone reporting ±15 m in the open and ±40 m between +/// buildings cannot resolve a 10 m fence at all, so the control stopped +/// measuring proximity and started measuring whether the satellites were kind. +/// That is the exact failure that got the fence switched off twice before. +/// +/// 100 m is the operational radius: comfortably outside normal GPS error, and +/// tight enough that a rider two streets away is refused. It is one number, +/// here, and it is deliberately **not** read from the server's `pickupradius` — +/// that made the app's one honesty control a per-tenant value nobody on this +/// side could see, and it silently disagreed with a hardcoded 500 in Home's own +/// gate. +const double kGeofenceRadiusMetres = 100; + +/// How stale a fix may be before it is not a measurement. +/// +/// On a round, a stale position is reliably the *previous* stop. Anything much +/// longer than this and the fence measures from the last door. +const Duration kGeofenceFixMaxAge = Duration(seconds: 30); + +/// The worst fix the fence will decide on, in metres. +/// +/// ── Rejected, not credited ── +/// +/// The previous implementation credited the phone's error to the rider — +/// `distance - accuracy > radius` — so a ±250 m fix bought 250 m of slack and a +/// rider a quarter-kilometre away was waved through. Against a 100 m fence that +/// inverts the control: the worse the fix, the easier it is to pass. +/// +/// A fix too vague to decide with is a fix the app must not decide with. At or +/// under this it is used raw; over it the rung is refused with "step into the +/// open", which is a thing the rider can act on. 50 m is generous against a +/// 100 m fence — typical urban Android fixes are 5–30 m — and it leaves the +/// fence measuring distance rather than luck. +const double kGeofenceMaxAccuracyMetres = 50; + +/// How long to wait for the GPS to settle before giving up on a live fix. +const Duration kGeofenceFixTimeout = Duration(seconds: 8); + +/// How long one live fix may serve several checks. +/// +/// ── Why this exists: a bulk action is ONE press at ONE place ── +/// +/// A rider ticking twenty bags at a counter and pressing "Picked up" produces +/// twenty calls to [Geofence.check], and without this each one took its own +/// `LocationAccuracy.best` fix with an 8-second ceiling. Twenty GPS settles for +/// a man who has not moved: 40–160 seconds of a rider standing at a counter +/// watching a spinner, which is most of the "status updates take two minutes" +/// report. +/// +/// Worse, the bulk arrival gate on Home worked around it with a 3-second +/// `.timeout(..., onTimeout: () => everything is inside the fence)`. Twenty +/// sequential fixes can never finish in three seconds, so that timeout fired +/// essentially every time and waved every order through — a hole straight +/// through the 100 m rule. One fix closes the hole by making the checks +/// instant, which is the better fix in both senses. +/// +/// ── Why fifteen seconds ── +/// +/// It is the window in which "where the rider is" has not meaningfully changed +/// against a 100 m fence. Walking pace is ~1.4 m/s, so fifteen seconds is about +/// 20 m of possible drift — a fifth of the radius, and well inside +/// [kGeofenceFixMaxAge], which already governs how old a position may be before +/// it stops being a measurement. Riding away from a stop at 30 km/h covers +/// 125 m in the same window, so a rider who genuinely leaves is outside the +/// fence on the next fix rather than the cached one: the cache is short enough +/// that it cannot carry him to the next door. +const Duration kGeofenceFixReuse = Duration(seconds: 15); + +/// Why the fence answered the way it did. +enum GeofenceOutcome { + /// Inside the radius. The only outcome that allows. + inside, + + /// Measured, and too far. [GeofenceDecision.distanceMetres] says how far. + outside, + + /// The booking carries no usable pin, so proximity cannot be checked. + /// + /// **Refused.** This used to be allowed-and-logged on the argument that the + /// rider cannot fix the hub's data from a doorstep. True, and it still made + /// the fence optional: a booking with a blank latitude opened every rung on + /// that stop. The office can fix a missing pin in a minute and the rider is + /// told to ring them. + noTarget, + + /// Location services are switched off on the handset. + serviceDisabled, + + /// The rider has not granted location permission. + permissionDenied, + + /// Permission is denied permanently; only Settings can restore it. + permissionDeniedForever, + + /// The GPS did not produce a fix in [kGeofenceFixTimeout] and there is no + /// recent cached one. + timeout, + + /// No fix at all, and not specifically a timeout. + noFix, + + /// The only fix available is older than [kGeofenceFixMaxAge]. + staleFix, + + /// The fix is worse than [kGeofenceMaxAccuracyMetres]. + poorAccuracy, + + /// The platform threw. Refused — see the class note on failing closed. + error, +} + +/// The fence's answer about one rung at one stop. +@immutable +class GeofenceDecision { + const GeofenceDecision({ + required this.allowed, + required this.outcome, + required this.timestamp, + this.distanceMetres, + this.accuracyMetres, + this.reason, + this.riderLat, + this.riderLng, + }); + + /// May the caller proceed to the API call? + /// + /// True for exactly one outcome — [GeofenceOutcome.inside]. Everything else + /// is a refusal, including every way of failing to measure. + final bool allowed; + + final GeofenceOutcome outcome; + + /// Metres from the rider to the stop, or null when it could not be measured. + final double? distanceMetres; + + /// The error radius the phone reported on the fix used, in metres. + final double? accuracyMetres; + + /// When the decision was taken. + final DateTime timestamp; + + /// The fix the decision was taken on, when there was one. + /// + /// Carried so a caller that has just been allowed through can stamp the same + /// position onto its payload instead of asking the GPS a second time. Two + /// fixes seconds apart are two different answers, and the one the fence + /// judged is the one the hub should be told about. + final double? riderLat; + final double? riderLng; + + /// One sentence, in the rider's words, naming what he must do. Null only when + /// [allowed] and there is nothing to say. + /// + /// Never a developer error, a status code, or a coordinate pair. + final String? reason; + + bool get isBlocked => !allowed; + + @override + String toString() => + 'GeofenceDecision(${outcome.name}, allowed=$allowed, ' + 'distance=${distanceMetres?.toStringAsFixed(1)}m, ' + 'accuracy=${accuracyMetres?.toStringAsFixed(0)}m)'; +} + +/// A position reading, or the reason there is not one. +@immutable +class GeofenceFix { + const GeofenceFix({this.position, this.failure}); + + /// A reading good enough to hand to the fence. + final Position? position; + + /// Why there is none. Ignored when [position] is set. + final GeofenceOutcome? failure; + + bool get hasPosition => position != null; +} + +/// Measures one rung against one stop. +/// +/// Stateless and static on purpose: Home's bulk gate and the per-stop rung in +/// `PickupsController` are different code paths that must never answer +/// differently, and the surest way to guarantee that is for there to be nothing +/// to construct differently. +abstract final class Geofence { + /// How the fence gets the rider's position. Swapped in tests. + /// + /// Kept as a settable function rather than a constructor argument because the + /// call sites are spread across controllers and widgets with no shared owner + /// to thread an instance through — and the alternative on offer was another + /// copy of the arithmetic. + static Future Function() positionProvider = _devicePosition; + + /// Restores the real device provider and drops any cached fix. + /// Call in `tearDown`. + static void useDevice() { + positionProvider = _devicePosition; + resetCache(); + } + + /// The last live fix, and when it was taken. See [kGeofenceFixReuse]. + static Position? _cachedFix; + static DateTime? _cachedAt; + + /// Forgets the cached fix. + /// + /// Called on sign-out and from tests. Also worth calling if a screen knows + /// the rider has travelled — though it should rarely be needed, because the + /// window is shorter than any journey between two stops. + static void resetCache() { + _cachedFix = null; + _cachedAt = null; + } + + /// True when a coordinate pair is a usable point on Earth. + /// + /// `0,0` is in the Gulf of Guinea and is what every unset latitude in this + /// codebase decays to, so it is treated as absent rather than as a place. + static bool isUsable(double? lat, double? lng) => + lat != null && + lng != null && + lat != 0 && + lng != 0 && + lat.abs() <= 90 && + lng.abs() <= 180; + + /// Metres between two points on the WGS-84 ellipsoid. + /// + /// Delegates to [Geolocator.distanceBetween] — never a latitude/longitude + /// comparison and never a flat-plane subtraction, both of which have been + /// tried in this app's history and are wrong by a factor that varies with + /// where the rider is standing. + static double distanceBetween( + double targetLat, + double targetLng, + double riderLat, + double riderLng, + ) => Geolocator.distanceBetween(targetLat, targetLng, riderLat, riderLng); + + /// The fence, for one [action] at one target. + /// + /// [action] is the rung's name in the rider's vocabulary — "Arrived", + /// "Delivered", "Handed over" — and is quoted back in + /// [GeofenceDecision.reason] so one sentence serves every rung. + static Future check({ + required double? targetLat, + required double? targetLng, + required String action, + double radiusMetres = kGeofenceRadiusMetres, + }) async { + final now = DateTime.now(); + final verb = action.toLowerCase(); + + // Logged with `debugPrint` in *every* build mode, deliberately. The whole + // risk of this switch is a build going out with proximity silently off, so + // the one thing it must never be is quiet. + if (!kGeofenceEnforced) { + debugPrint( + '[GEOFENCE] OFF for $action — this build was compiled with ' + 'ENFORCE_GEOFENCE=false. Stop completion is NOT proximity-verified.', + ); + return GeofenceDecision( + allowed: true, + outcome: GeofenceOutcome.inside, + timestamp: now, + ); + } + + // ── No pin on the booking: REFUSED ── + // + // Checked before the phone is asked for anything, because no fix can answer + // a question with no target and the rider should not wait 8s to be told so. + if (!isUsable(targetLat, targetLng)) { + final decision = GeofenceDecision( + allowed: false, + outcome: GeofenceOutcome.noTarget, + timestamp: now, + reason: + 'This stop has no map location on it, so Miler cannot confirm you ' + 'are there. Ring your office and ask them to add the address ' + 'location.', + ); + debugPrint( + '[GEOFENCE] $action refused: stop carries no coordinates ' + '($targetLat, $targetLng) — data gap on the booking', + ); + return decision; + } + + final GeofenceFix fix; + try { + fix = await positionProvider(); + } catch (e) { + // ── A throw is a refusal ── + // + // The old implementation caught here and returned `true` with the comment + // "allow on error (fail-safe)". That is fail-*open*: any throw from the + // location stack — and the permission plugin throws rather than returning + // on several OEM builds — opened every rung in the app. + debugPrint('[GEOFENCE] $action refused: position provider threw: $e'); + return GeofenceDecision( + allowed: false, + outcome: GeofenceOutcome.error, + timestamp: now, + reason: _reasonForFixFailure(GeofenceOutcome.error, verb), + ); + } + + if (!fix.hasPosition) { + final outcome = fix.failure ?? GeofenceOutcome.noFix; + final decision = GeofenceDecision( + allowed: false, + outcome: outcome, + timestamp: now, + reason: _reasonForFixFailure(outcome, verb), + ); + debugPrint('[GEOFENCE] $action refused: $decision'); + return decision; + } + + final pos = fix.position!; + final accuracy = pos.accuracy; + + // ── The rider's own position must be a place ── + // + // A plugin that answers `0,0` rather than throwing is what this catches; + // without it the fence measures from the Gulf of Guinea and refuses every + // stop in India with a distance in the thousands of kilometres. + if (!isUsable(pos.latitude, pos.longitude)) { + final decision = GeofenceDecision( + allowed: false, + outcome: GeofenceOutcome.noFix, + timestamp: now, + accuracyMetres: accuracy, + reason: _reasonForFixFailure(GeofenceOutcome.noFix, verb), + ); + debugPrint('[GEOFENCE] $action refused: $decision'); + return decision; + } + + final distance = distanceBetween( + targetLat!, + targetLng!, + pos.latitude, + pos.longitude, + ); + + // ── A fix too vague to decide with ── + // + // Refused rather than credited. Checked *after* the distance is computed so + // the decision still carries the measurement for the log, but before the + // radius comparison so a vague fix can never pass one. See + // [kGeofenceMaxAccuracyMetres]. + if (accuracy > kGeofenceMaxAccuracyMetres) { + final decision = GeofenceDecision( + allowed: false, + outcome: GeofenceOutcome.poorAccuracy, + timestamp: now, + distanceMetres: distance, + accuracyMetres: accuracy, + riderLat: pos.latitude, + riderLng: pos.longitude, + reason: + 'Your phone is not sure where you are yet. Step into the open for a ' + 'moment, then mark this stop $verb.', + ); + debugPrint('[GEOFENCE] $action refused: $decision'); + return decision; + } + + // ── Raw distance against the radius ── + // + // Not `distance - accuracy`. The slack that arithmetic granted grew with + // the phone's own error, so the worse the fix the further out a rider could + // stand — the control running backwards. The error is handled once, above, + // by refusing to decide on a fix that cannot resolve 100 m. + final inside = distance <= radiusMetres; + + final decision = GeofenceDecision( + allowed: inside, + outcome: inside ? GeofenceOutcome.inside : GeofenceOutcome.outside, + timestamp: now, + distanceMetres: distance, + accuracyMetres: accuracy, + riderLat: pos.latitude, + riderLng: pos.longitude, + reason: inside ? null : _tooFarSentence(distance, radiusMetres, verb), + ); + debugPrint( + '[GEOFENCE] $action | target ($targetLat, $targetLng) ' + '| rider (${pos.latitude}, ${pos.longitude}) | $decision', + ); + return decision; + } + + /// "You're 240 m away. Move within 100 m to mark this stop arrived." + /// + /// Distance first, because that is the number the rider acts on, and in the + /// unit he thinks in — metres under a kilometre, kilometres over it. + static String _tooFarSentence(double distance, double radius, String verb) { + final away = distance >= 1000 + ? '${(distance / 1000).toStringAsFixed(1)} km' + : '${distance.round()} m'; + return "You're $away away. Move within ${radius.round()} m to mark this " + 'stop $verb.'; + } + + static String _reasonForFixFailure(GeofenceOutcome outcome, String verb) => + switch (outcome) { + GeofenceOutcome.serviceDisabled => + 'Location is switched off on your phone. Turn it on, then mark this ' + 'stop $verb.', + GeofenceOutcome.permissionDenied => + 'Miler needs your location to confirm you are at this stop. Allow ' + 'location access, then mark it $verb.', + GeofenceOutcome.permissionDeniedForever => + 'Location access is blocked for Miler. Open Settings → Permissions → ' + 'Location and allow it, then mark this stop $verb.', + GeofenceOutcome.staleFix => + 'Your phone last found you a while ago. Wait a moment for it to catch ' + 'up, then mark this stop $verb.', + GeofenceOutcome.timeout => + 'Your phone is still looking for your location. Step into the open, ' + 'wait a moment, then mark this stop $verb.', + _ => + 'Your phone could not find your location, so this stop cannot be ' + 'marked $verb. Turn location on, allow it for Miler, and step ' + 'outside if you can.', + }; + + /// The real device fix: service, permission, then the best reading the + /// hardware will give inside [kGeofenceFixTimeout]. + /// + /// Falls back to a cached position **only** when it is fresher than + /// [kGeofenceFixMaxAge]; a last-known fix has no age limit of its own and on + /// a round it is reliably the previous stop. + static Future _devicePosition() async { + // ── One press at one place is one fix ── + // + // Checked before the service and permission calls, because those are + // platform-channel round trips too and a batch of twenty pays them twenty + // times for an answer that cannot have changed. A cached fix is only ever + // stored after a *successful* live read, so this can never serve a stale or + // vague one — the age check below is the only way in. + final cached = _cachedFix; + final at = _cachedAt; + if (cached != null && + at != null && + DateTime.now().difference(at) <= kGeofenceFixReuse) { + return GeofenceFix(position: cached); + } + + try { + if (!await Geolocator.isLocationServiceEnabled()) { + return const GeofenceFix(failure: GeofenceOutcome.serviceDisabled); + } + + var permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + } + if (permission == LocationPermission.deniedForever) { + return const GeofenceFix( + failure: GeofenceOutcome.permissionDeniedForever, + ); + } + if (permission == LocationPermission.denied) { + return const GeofenceFix(failure: GeofenceOutcome.permissionDenied); + } + + try { + final pos = await Geolocator.getCurrentPosition( + locationSettings: const LocationSettings( + // `best`, not `low`. A `low` fix is a ~1 km hint on Android; against + // a 100 m fence that is not a degraded measurement, it is noise + // being treated as evidence. + accuracy: LocationAccuracy.best, + timeLimit: kGeofenceFixTimeout, + ), + ); + _cachedFix = pos; + _cachedAt = DateTime.now(); + return GeofenceFix(position: pos); + } catch (_) { + // No live fix. A recent cached one is a measurement; a stale one is not. + final cached = await Geolocator.getLastKnownPosition(); + if (cached == null) { + return const GeofenceFix(failure: GeofenceOutcome.timeout); + } + final age = DateTime.now().difference(cached.timestamp); + if (age > kGeofenceFixMaxAge) { + debugPrint( + '[GEOFENCE] cached fix is ${age.inSeconds}s old — too stale to ' + 'measure a ${kGeofenceRadiusMetres.round()}m fence', + ); + return const GeofenceFix(failure: GeofenceOutcome.staleFix); + } + return GeofenceFix(position: cached); + } + } catch (e) { + debugPrint('[GEOFENCE] position lookup threw: $e'); + return const GeofenceFix(failure: GeofenceOutcome.error); + } + } +} diff --git a/lib/data/miler_api.dart b/lib/data/miler_api.dart index cb5d245..2b7c36e 100644 --- a/lib/data/miler_api.dart +++ b/lib/data/miler_api.dart @@ -336,11 +336,14 @@ class MilerApi { } // ═══════════════════════════════════════════════════════════════════════ - // AUTH (3 routes — reset-pin is intentionally not one of them) + // AUTH (4 routes — reset-pin is intentionally not one of them) // ═══════════════════════════════════════════════════════════════════════ /// Step 1. 404 when there is no account, 403 when the row is not role 5 or /// not Active — both surface as `ok: false` with the server's message. + /// + /// Answers `{success, message, phone, pin_set}` — **top level, not under + /// `data`**. Read the branch with [pinSetOf]; never off the message string. static Future login(String phone) => _send( 'POST', '/miler/login', @@ -352,6 +355,79 @@ class MilerApi { }, ); + /// Does this account already have a PIN on file? + /// + /// ── The one thing the login response is asked for ── + /// + /// Riders set their own PIN on first sign-in now; the console no longer + /// issues one. `pin_set` is how the server says which of the two screens the + /// rider is owed, and it is a **boolean** — the message beside it is prose + /// and prose gets reworded. Branching on "PIN verification required" would + /// break the day somebody improves that sentence. + /// + /// Null when the response did not carry the field at all: an older server, or + /// a failure. A caller that cannot tell should send the rider to Enter-PIN, + /// which is the safe direction — a rider who does have a PIN can use it, and + /// one who does not gets a refusal he can report, rather than being walked + /// into a Set-PIN screen that will 409. + static bool? pinSetOf(ApiResult res) { + final raw = res.raw; + final v = raw is Map ? (raw['pin_set'] ?? raw['pinSet']) : null; + if (v is bool) return v; + if (v is String) { + final t = v.toLowerCase(); + if (t == 'true') return true; + if (t == 'false') return false; + } + return null; + } + + /// First-time PIN creation, self-service. `POST /miler/set-pin`. + /// + /// ── This route did not exist, and its absence shaped the whole screen ── + /// + /// The only PIN-write the backend had was `POST /miler/reset-pin`, which + /// needs an ADMIN token — it was once open, and reset-pin followed by + /// verify-pin took over any rider account given nothing but a phone number. + /// So the app could not let a rider set a PIN at all, and `AuthProvider` + /// answered its own Create-MPIN screen with a manufactured 403 telling him + /// his office issues it. + /// + /// That is no longer true. This route is rider-authenticated by phone, + /// **cannot overwrite an existing PIN** (409 if one is set), and returns a + /// full session — the same `{token, user}` shape as [verifyPin] — so the + /// rider lands signed in without a second round trip. + /// + /// Refusals: `404` no such phone, `403` inactive or not a miler, `409` a PIN + /// already exists — send that rider to Enter-PIN instead. + static Future setPin({ + required String phone, + required String pin, + String? deviceToken, + }) async { + final res = await _send( + 'POST', + '/miler/set-pin', + auth: false, + body: { + 'phone': phone, + 'new_pin': pin, + 'configid': configId, + if (hasTenantId) 'tenantid': tenantId, + if (deviceToken != null && deviceToken.isNotEmpty) + 'device_token': deviceToken, + }, + ); + // Same token handling as verify-pin, deliberately: this IS a sign-in, and + // a second implementation of "where does the session come from" is how the + // two paths drift. + if (res.ok && res.raw is Map) { + final token = _str((res.raw as Map)['token']); + if (token.isNotEmpty) await ApiConfig.setToken(token); + } + return res; + } + /// Step 2. On success the token is stored and the caller gets `user`. /// /// The login is under `user` / `user.profile` — there is no `data` key on @@ -1001,10 +1077,24 @@ class MilerApi { /// /// The signature expires in ten minutes, so a failed upload is re-*signed* /// rather than retried against the old URL. + /// ── A pickup proof has no consignment, and must not pretend to ── + /// + /// The server builds the storage key as `{folder}/{tag}-{id}-…`, choosing the + /// id from `consignmentid`, then `bookingid`, then the rider. A pickup photo + /// is taken *before* `pickup-complete` mints anything, so there is no + /// consignment to key it on — and this method only offered `consignmentid`. + /// The one caller that needed it passed a **booking** id in that slot, so + /// every pickup proof this app has ever uploaded was filed under a + /// consignment number that does not exist, colliding with whatever real + /// consignment later took it. + /// + /// [bookingId] is the field the server already has for this case. Send + /// whichever one the stop actually has. static Future signUpload({ required String purpose, String contentType = 'image/jpeg', Object? consignmentId, + Object? bookingId, }) => _send( 'POST', '/miler/uploads/sign', @@ -1012,6 +1102,7 @@ class MilerApi { 'purpose': purpose, 'contentType': contentType, if (consignmentId != null) 'consignmentid': consignmentId, + if (bookingId != null) 'bookingid': bookingId, }, ); @@ -1034,6 +1125,35 @@ class MilerApi { File file, { required String purpose, Object? consignmentId, + Object? bookingId, + }) async => + (await uploadProofRef( + file, + purpose: purpose, + consignmentId: consignmentId, + bookingId: bookingId, + ))?.url; + + /// The same upload, returning **both** references the platform uses. + /// + /// ── Why the key matters, and why it was being thrown away ── + /// + /// `POST /miler/uploads/sign` answers with `uploadurl`, `url` **and `key`**. + /// This method used to keep only `url` and discard the key, which was fine + /// while the only consumer was `deliver` — that route takes a `photourl`. + /// + /// It is not fine for `POST /miler/bookings/:id/parcel`, whose `photos` field + /// wants the **storage key**, not a URL. The server's own note says why: the + /// customer is served a short-lived signed link *derived from* the key, never + /// a permanent one. Sending a URL there would store a link that either + /// expires or, worse, never does. + /// + /// So both come back and each caller takes the one its route is specified in. + static Future uploadProofRef( + File file, { + required String purpose, + Object? consignmentId, + Object? bookingId, }) async { if (!file.existsSync()) return null; @@ -1045,6 +1165,7 @@ class MilerApi { purpose: purpose, contentType: contentType, consignmentId: consignmentId, + bookingId: bookingId, ); if (!signed.ok) { debugPrint('[UPLOAD] sign failed: ${signed.status} ${signed.message}'); @@ -1057,6 +1178,7 @@ class MilerApi { .toString() .trim(); final publicUrl = (map?['url'] ?? '').toString().trim(); + final objectKey = (map?['key'] ?? '').toString().trim(); if (uploadUrl.isEmpty || publicUrl.isEmpty) { debugPrint('[UPLOAD] sign returned no url pair'); return null; @@ -1078,7 +1200,9 @@ class MilerApi { body: await file.readAsBytes(), ) .timeout(const Duration(seconds: 30)); - if (res.statusCode >= 200 && res.statusCode < 300) return publicUrl; + if (res.statusCode >= 200 && res.statusCode < 300) { + return UploadRef(url: publicUrl, key: objectKey); + } debugPrint('[UPLOAD] PUT ${res.statusCode} — ${res.body}'); } catch (e) { debugPrint('[UPLOAD] PUT failed: $e'); @@ -1169,7 +1293,10 @@ class MilerApi { if (heading != null) 'heading': _str(heading), if (accuracy != null) 'accuracy': _str(accuracy), if (status != null && status.isNotEmpty) 'status': status, - if (orderId != null) 'orderid': _str(orderId), + // The device's own stop key can carry a `#seq` destination suffix (see + // ApiConfig's adapter); the server has never seen one and this is a + // breadcrumb tag, not a join key. Send the booking's half. + if (orderId != null) 'orderid': _str(orderId).split('#').first, if (battery != null) 'battery': _str(battery), 'is_charging': isCharging, if (connection != null) 'connection': connection, @@ -1298,6 +1425,26 @@ class MilerApi { /// Dimensions are centimetres and weight is kilograms. They are what /// `pickup-complete` recomputes chargeable weight from, so a parcel submitted /// without them bills on the booked figure rather than the real one. +/// Where an uploaded image lives, in the two forms the platform uses. +/// +/// `deliver` takes [url] as `photourl`; `parcel` takes [key] as one entry of +/// `photos`. Neither is a substitute for the other — see [MilerApi.uploadProofRef]. +@immutable +class UploadRef { + const UploadRef({required this.url, required this.key}); + + /// The object's public URL. + final String url; + + /// The object's storage key — `{folder}/{tag}-{id}-{date}-{time}-{rand}.{ext}`. + /// + /// May be empty if an older server build does not return one; a caller that + /// needs it must treat empty as "no photo" rather than sending a blank entry. + final String key; + + bool get hasKey => key.isNotEmpty; +} + class ParcelEntry { final Object? parcelId; final double weight; @@ -1305,12 +1452,33 @@ class ParcelEntry { final double width; final double height; + /// Storage keys of the photographs taken of this parcel at the door. + /// + /// ── The field the app never sent ── + /// + /// `POST /miler/bookings/:id/parcel` has accepted `photos` for as long as the + /// route has existed, and the server's own note says why it matters: *"Weight + /// without a photograph is a number the customer has no way to check, and + /// this is the only point in the flow where anyone is standing next to the + /// parcel."* + /// + /// Meanwhile the rider app **compels** the photograph — `StopVerificationPage` + /// will not let him confirm without one — uploaded it to object storage, and + /// then dropped the reference on the floor. The evidence existed in a bucket + /// nobody could search, for every CX pickup this app has ever done. + /// + /// **Keys, not URLs.** The customer is served a short-lived signed link + /// derived from the key; a URL stored here either expires or never does. + /// See [MilerApi.uploadProofRef]. + final List photos; + const ParcelEntry({ this.parcelId, required this.weight, this.length = 0, this.width = 0, this.height = 0, + this.photos = const [], }); Map toJson() => { @@ -1319,6 +1487,10 @@ class ParcelEntry { 'length': length, 'width': width, 'height': height, + // Omitted entirely when there is none, rather than sent as `[]`: an empty + // array is a claim that no photograph was taken, and a failed upload is + // not that claim. + if (photos.isNotEmpty) 'photos': photos, }; } diff --git a/lib/data/milk_run.dart b/lib/data/milk_run.dart index 4b9c9ba..2de4fe9 100644 --- a/lib/data/milk_run.dart +++ b/lib/data/milk_run.dart @@ -78,9 +78,68 @@ class MilkRun { static const bool deliveryArrivalIsLocalOnly = true; /// Order id of a stop, however the payload spells it. + /// + /// On a customer-app pickup with several destinations this is the **stop** + /// key, not the booking's — `DM-482913#1` — because everything the device + /// remembers is filed under it and three doors sharing one key means three + /// doors sharing one memory. The suffix is added by `ApiConfig` where the row + /// is adapted; see the note there. [bookingKeyOf] is the plain booking key, + /// and `bookingreference` on the row is what the rider is shown. static String idOf(Map stop) => (stop['orderid'] ?? stop['orderId'] ?? '').toString(); + /// The booking this stop belongs to, with any destination suffix removed. + /// + /// Used where the fact being recorded is about the **visit** rather than the + /// drop. Collection is the case that matters: the rider collects a whole + /// pickup once, so the collected set is written with the booking key while + /// the delivery stops that come out of it each carry their own. See + /// [wasCollected]. + static String bookingKeyOf(Map stop) { + final raw = idOf(stop); + final cut = raw.indexOf('#'); + if (cut > 0) return raw.substring(0, cut); + final ref = (stop['bookingreference'] ?? '').toString().trim(); + return ref.isNotEmpty ? ref : raw; + } + + /// How many doors this one pickup visit is for. 1 for everything but a + /// multi-destination customer-app booking. + static int destinationCountOf(Map stop) => + int.tryParse((stop['destinationcount'] ?? '').toString()) ?? 1; + + /// Which door of the visit this stop is, counting from 0. + static int destinationSeqOf(Map stop) => + int.tryParse((stop['destinationseq'] ?? '').toString()) ?? 0; + + /// `Stop 2 of 3`, or `''` when the pickup has only one door. + /// + /// Empty rather than `Stop 1 of 1` deliberately: a label that appears on + /// every card in the app stops being read, and the fact it carries — this + /// bag is one of several from the same counter — is only ever news when + /// there are several. + static String stopLabel(Map stop) { + final count = destinationCountOf(stop); + if (count <= 1) return ''; + return 'Stop ${destinationSeqOf(stop) + 1} of $count'; + } + + /// True when the visit this stop came from has been collected. + /// + /// Asks for the stop's own key first and the booking's second, and the second + /// question is the one that matters. A multi-destination pickup is collected + /// **once**, under the booking key, while it is still a single pre-pickup + /// row; the poll after `pickup-complete` then replaces that row with one per + /// door, each carrying a key the collected set has never seen. Asking only + /// the stop key would drop every one of those bags back to "not collected" + /// at the moment the rider actually had them in his hands. + static bool wasCollected( + Map stop, + Set collectedIds, + ) => + collectedIds.contains(idOf(stop)) || + collectedIds.contains(bookingKeyOf(stop)); + /// The consignment this stop became once it was collected, or '' before that. /// /// The delivery route keys on this and not on the booking id — they come from @@ -326,7 +385,7 @@ class MilkRun { // this function did before and is still right for a milk run, whose rows // often carry no consignment state at all. if (!ServiceProfile.active.deliversToCustomer) return false; - if (collectedIds.contains(idOf(stop))) return true; + if (wasCollected(stop, collectedIds)) return true; final status = stopStatusOf(stop); return status.isPicked || status.isDeliveryLeg; } diff --git a/lib/data/mock_backend.dart b/lib/data/mock_backend.dart index 2ca040d..ec120f1 100644 --- a/lib/data/mock_backend.dart +++ b/lib/data/mock_backend.dart @@ -68,6 +68,19 @@ import 'package:flutter/foundation.dart'; /// flutter run --dart-define=MOCK_BACKEND=true # the canned day const bool kMockBackend = bool.fromEnvironment('MOCK_BACKEND'); +/// One customer pickup with three doors, for looking at. +/// +/// **Off unless asked for**, and separate from [kMockBackend] on purpose. The +/// bookings route above is deliberately empty — "inventing one here would put +/// fictional consignments in front of a parcel rider" — and that stays exactly +/// true by default. This is the one shape that cannot be reached any other way: +/// `GET /miler/bookings` only returns a row per destination once a +/// multi-destination customer-app booking has been collected, so seeing the +/// three-door card on a bench needs either a seeded backend or this. +/// +/// flutter run --dart-define=MOCK_BACKEND=true --dart-define=MOCK_MULTIDROP=true +const bool kMockMultiDrop = bool.fromEnvironment('MOCK_MULTIDROP'); + /// Canned answers for the routes the rider's day passes through. /// /// Everything else gets a generic success, on purpose: the alternative is a @@ -108,6 +121,103 @@ class MockBackend { /// of it and no log line makes it look like a real session. static const String token = 'MOCK-TOKEN-not-a-real-session'; + /// One collected customer pickup, as three rows — the shape + /// `milerStopsForBooking` returns once `pickup-complete` has fanned a + /// multi-destination booking out into a consignment per door. + /// + /// Written in the **raw wire shape**, not the app's stop shape, because that + /// is what the mock intercepts: these go through `ApiConfig.pickupFromBooking` + /// exactly as a real response would, so the adapter is being exercised rather + /// than bypassed. Same `bookingid` and `bookingreference` on all three, which + /// is the whole point — they are told apart only by `destinationseq`. + /// + /// Ids are `MOCK-` per rule 3 so `purgeDemoRecords()` can find anything that + /// leaks into a store. + static const List> multiDropVisit = [ + { + 'bookingid': 900482913, + 'bookingreference': 'MOCK-482913', + 'status': 'Converted_To_Consignment', + 'consignmentid': 900500, + 'consignmentstatus': 'Out_for_Delivery', + 'trackingno': 'MOCK-TRK-8841020', + 'destinationseq': 0, + 'destinationcount': 3, + 'next_action': 'deliver', + 'step': 1, + 'pickup_source_type': 'customer', + 'pickup_source_name': 'Ravi Kumar', + 'pickupaddress': '14, Cross Cut Road, Gandhipuram, Coimbatore 641012', + 'pickuplatitude': 11.0183, + 'pickuplongitude': 76.9725, + 'deliveryaddress': '22, Anna Nagar West, Chennai, Tamil Nadu', + 'deliverylatitude': 11.0210, + 'deliverylongitude': 76.9810, + 'recipientname': 'Anitha Raghavan', + 'recipientphone': '9840012201', + 'customername': 'Ravi Kumar', + 'customerphone': '9843311220', + 'parcels': [ + {'bookingparcelid': 900001, 'itemcategory': 'General'}, + {'bookingparcelid': 900002, 'itemcategory': 'General'}, + ], + }, + { + 'bookingid': 900482913, + 'bookingreference': 'MOCK-482913', + 'status': 'Converted_To_Consignment', + 'consignmentid': 900501, + 'consignmentstatus': 'Out_for_Delivery', + 'trackingno': 'MOCK-TRK-8841021', + 'destinationseq': 1, + 'destinationcount': 3, + 'next_action': 'deliver', + 'step': 2, + 'pickup_source_type': 'customer', + 'pickup_source_name': 'Ravi Kumar', + 'pickupaddress': '14, Cross Cut Road, Gandhipuram, Coimbatore 641012', + 'pickuplatitude': 11.0183, + 'pickuplongitude': 76.9725, + 'deliveryaddress': '7, Panampilly Nagar, Ernakulam, Kerala', + 'deliverylatitude': 11.0301, + 'deliverylongitude': 76.9902, + 'recipientname': 'Suresh Menon', + 'recipientphone': '9847712345', + 'customername': 'Ravi Kumar', + 'customerphone': '9843311220', + 'parcels': [ + {'bookingparcelid': 900003, 'itemcategory': 'General'}, + ], + }, + { + 'bookingid': 900482913, + 'bookingreference': 'MOCK-482913', + 'status': 'Converted_To_Consignment', + 'consignmentid': 900502, + 'consignmentstatus': 'Out_for_Delivery', + 'trackingno': 'MOCK-TRK-8841022', + 'destinationseq': 2, + 'destinationcount': 3, + 'next_action': 'deliver', + 'step': 3, + 'pickup_source_type': 'customer', + 'pickup_source_name': 'Ravi Kumar', + 'pickupaddress': '14, Cross Cut Road, Gandhipuram, Coimbatore 641012', + 'pickuplatitude': 11.0183, + 'pickuplongitude': 76.9725, + 'deliveryaddress': '90, Indiranagar 2nd Stage, Bengaluru, Karnataka', + 'deliverylatitude': 11.0405, + 'deliverylongitude': 77.0011, + 'recipientname': 'Meena Prakash', + 'recipientphone': '9880045512', + 'customername': 'Ravi Kumar', + 'customerphone': '9843311220', + 'parcels': [ + {'bookingparcelid': 900004, 'itemcategory': 'General'}, + ], + }, + ]; + /// Answers [method] [path], or null when the caller should fall through to /// the network. Null is never returned while [enabled] — see the class note. static Map? respond( @@ -160,7 +270,7 @@ class MockBackend { // at all. A parcel booking list has no fixture and inventing one here would // put fictional consignments in front of a parcel rider. if (route.endsWith('/bookings') || route.endsWith('/assignments')) { - return _ok(const []); + return _ok(kMockMultiDrop ? multiDropVisit : const []); } // ── Everything the rider writes ── diff --git a/lib/data/session.dart b/lib/data/session.dart new file mode 100644 index 0000000..40643c7 --- /dev/null +++ b/lib/data/session.dart @@ -0,0 +1,77 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:miler/data/accepted_store.dart'; +import 'package:miler/data/api_config.dart'; +import 'package:miler/data/geofence.dart'; +import 'package:miler/data/mutation_guard.dart'; +import 'package:miler/data/proof_store.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// ENDING A SESSION, IN ONE PLACE +/// +/// A rider's session ends two ways, and until now only one of them was +/// implemented: +/// +/// * **He presses Log out.** The Account screen tore everything down — +/// scoped stores, doorstep photos, bearer token, in-flight guard, cached +/// fix — raised `logged_out` and sent him to sign-in. +/// * **The server stops accepting his token.** `MilerApi` drops the token on +/// any 401 and calls `onUnauthorized`, which **nothing ever assigned**. So +/// the credential vanished and nothing else did. +/// +/// The second case left a state that looks signed in and cannot work: the +/// profile is still in SharedPreferences, `logged_out` is still `false`, so +/// the app draws the rider's own name over a dashboard whose every call comes +/// back `401 authorization header is required`. Observed on a real handset — +/// Karthikeyan CBE Rider, rider 23, name and tenant on screen, `authtoken` +/// absent from disk, Home / Deliveries / Activity and the background heartbeat +/// all failing in a loop. +/// +/// What that costs is not a blank screen. A rider opens the app, sees himself +/// signed in and sees no jobs, and concludes there is no work today — there is +/// no prompt anywhere telling him to sign in again. He waits; the stops go +/// undelivered; support cannot tell over the phone. +/// +/// So the teardown lives here, both callers use it, and neither can drift from +/// the other. The rule it encodes: **the credential and the appearance of +/// being signed in end together, always.** +/// ───────────────────────────────────────────────────────────────────────── + +/// Tears down everything this device holds about the signed-in rider. +/// +/// Safe to call twice — every step is idempotent — which matters because a 401 +/// rarely arrives alone: the home poll, the deliveries queue and the heartbeat +/// can all get one within the same second. +/// +/// Deliberately knows nothing about navigation. Where the rider goes next is a +/// UI decision and belongs to the caller; this is the part that must happen +/// identically whether he chose to leave or was shown the door. +Future endSession() async { + // Finished work, skipped stops, carried bags and bag labels are scoped per + // rider/tenant/line — this drops *this* scope so nothing survives into the + // next rider's session on a shared handset. + await clearScopedStores(); + + // Doorstep photos are exactly the kind of record that must not outlive the + // session that took them. + await ProofStore.clearScope(); + + // The credential goes with the session, not at the next sign-in. Anything + // that reads the token without checking a flag — a background isolate, the + // notification handler, a heartbeat that outlives a route change — must not + // be able to keep calling as the rider who has left. + await ApiConfig.clearToken(); + + // A stale in-flight mutation must not find a key still held from the session + // being closed. + MutationGuard.reset(); + + // A cached position is a fact about a rider and must not outlive him: the + // next person to sign in would have his first proximity check measured from + // wherever the last rider was standing. + Geofence.resetCache(); + + // The flag the launch path reads to decide between Home and sign-in. + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool('logged_out', true); +} diff --git a/lib/data/task_profile.dart b/lib/data/task_profile.dart new file mode 100644 index 0000000..653264c --- /dev/null +++ b/lib/data/task_profile.dart @@ -0,0 +1,446 @@ +import 'package:flutter/foundation.dart'; + +import 'package:miler/data/next_leg.dart'; +import 'package:miler/data/service_profile.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// WHAT **THIS ONE JOB** REQUIRES — asked of the job, never of the rider +/// +/// There is one kind of Miler. The same rider can be carrying ten meal parcels +/// out of a kitchen, five customer pickups booked in the Doormile app, and a +/// hub handover, all inside one shift. So nothing about a rider can decide how +/// a stop behaves: a rider is not "a milk man" or "a logistics rider", he is a +/// person with a queue, and every item in that queue brings its own workflow. +/// +/// This is the replacement for reading [ServiceProfile.active]. That static +/// answers "who is this rider", which is the wrong question and gives the same +/// answer for every row on the screen — so a customer pickup sitting next to a +/// kitchen collection got the kitchen's rules, or vanished. +/// +/// ── Read, never inferred ── +/// +/// Every field below is derived from what the **server said about this row**: +/// `pickup_source_type` and `next_action`, both already on `GET /miler/bookings` +/// and both already parsed ([PickupSource], [NextLeg]). Nothing here looks at a +/// tenant, a business name, an address, a pincode or a missing id. Where the +/// server has not said, the answer is [WorkKind.unknown] and the screens draw +/// the plain, safe shape — the same doctrine [PickupSource] already states: +/// the server's word, or an honest "not known", never a guess. +/// ───────────────────────────────────────────────────────────────────────── +enum WorkKind { + /// Collect in bulk from a place of business — a kitchen, a base, a shop. + /// The meal round's morning load and an ordinary hub collection are the same + /// shape of work: one address, a manifest, many bags. + sourcePickup, + + /// Collect from a person's front door, against a booking that customer made + /// in the Doormile app. Needs the verify-at-the-door flow: count what is + /// actually there, weigh it, capture the destination(s), take payment if the + /// booking asks for it. + customerPickup, + + /// Already in the rider's hands, going to a receiver. + customerDelivery, + + /// Already in the rider's hands, going to a base. The network carries it from + /// there — this rider does not ride it to another city. + hubHandover, + + /// Nothing left for this rider on this item. + done, + + /// In custody, and the server has not said where it goes. Deliberately not + /// folded into [customerDelivery] or [done]: see [NextLeg.unknown], which + /// makes the same call for the same reason. + unknown, +} + +/// The capabilities one work item needs, resolved from that item alone. +@immutable +class TaskProfile { + const TaskProfile._({ + required this.kind, + required this.groupsBySource, + required this.leavesHomeOnAccept, + required this.deliversToCustomer, + required this.endsAtHub, + required this.capturesShipmentDetails, + required this.mayCollectCash, + }); + + /// What kind of work this row is. + final WorkKind kind; + + /// This stop is collected in bulk from a named place of business, so Home can + /// head it with that place — a kitchen load is one visit, not fourteen rows. + /// + /// ── NOT a drop-in for `ServiceProfile.active.sourceIsKitchen` ── + /// + /// Read this before migrating a grouping call site. `sourceIsKitchen` looks + /// like "does this group?" and is not: it **selects which key to group by**. + /// + /// • On the milk round, stops group under the **source they were collected + /// from** — the kitchen. + /// • On logistics, stops group under the **base that assigned the work**, + /// drops and collections alike, *including a collection made at a + /// customer's own door*. `hub_pickup_group_test.dart` states the rule: + /// "one building assigned them, so one node holds them". + /// + /// Substituting this flag for that one splits a single hub run into two + /// groups — a customer collection stops keying to the assigning base and + /// wanders off under its own name. That was tried, and those tests caught it. + /// + /// ── And DO NOT migrate a grouping site to this flag ── + /// + /// This was attempted twice and reverted twice. The second attempt is the + /// instructive one: with the safe default below, `hub_pickup_group_test.dart` + /// went green and the **milk round lost every kitchen heading** — + /// `service_flow_test.dart` reports `Found 0 widgets with text "Kitchen 2"`. + /// + /// The reason is the thing to understand before trying again: **the rows do + /// not carry the discriminator**. Neither the meal fixtures nor the logistics + /// fixtures set `pickup_source_type`, so per-stop classification cannot tell + /// a kitchen load from a customer door — both land on [WorkKind.unknown]. + /// `ServiceProfile.active.sourceIsKitchen` was not merely *deciding* the + /// grouping; it was *supplying information the payload lacks*. Removing it + /// removes the fact, and no default can be right for both lines at once: + /// grouping by default breaks logistics, not grouping breaks the milk round. + /// + /// What a grouping site actually needs is the stop's own **source identity**, + /// which the rows DO carry — `sourcename`, `pickuplocationid`, `sourceid`. + /// `MilkRun.sourceKeyOf` already reads exactly those and returns `id:…` or + /// `name:…` for a stop with a real named counter, falling back to `at:…` or + /// `none` for one without. That distinction is the grouping key, and it works + /// on today's payloads. Build on it, not on this flag. + /// + /// This flag stays honest about its own narrow claim: "the server said this + /// is collected from a business". It is correct for rows that carry + /// `pickup_source_type`, and it is silent — not wrong — for rows that do not. + final bool groupsBySource; + + /// Whether accepting moves it off Home immediately. + /// + /// A customer pickup does: it becomes an appointment the rider owes someone, + /// and it belongs on Bookings until he has collected it. A source pickup does + /// not: accepting a kitchen load does not put anything in his hands, so it + /// stays on Home until he is actually carrying it. + /// + /// Replaces `ServiceProfile.active.handoffAt`. + final bool leavesHomeOnAccept; + + /// Finishes at a receiver's door. + final bool deliversToCustomer; + + /// Finishes by handing over at a base. + final bool endsAtHub; + + /// The door flow has to capture what is being shipped and where it is going — + /// package count, weights, destinations, recipients. Only a customer pickup + /// does this; a kitchen manifest is already known before the rider arrives. + final bool capturesShipmentDetails; + + /// Money may change hands on this item. Whether it actually does is the + /// row's own COD figure — this only says the flow must be able to. + final bool mayCollectCash; + + /// The safe shape: a plain stop, no grouping, no door capture, no promises + /// about where it ends. + static const TaskProfile unknownTask = TaskProfile._( + kind: WorkKind.unknown, + groupsBySource: false, + leavesHomeOnAccept: false, + deliversToCustomer: false, + endsAtHub: false, + capturesShipmentDetails: false, + mayCollectCash: false, + ); + + static const TaskProfile sourcePickup = TaskProfile._( + kind: WorkKind.sourcePickup, + groupsBySource: true, + leavesHomeOnAccept: false, + deliversToCustomer: false, + endsAtHub: false, + capturesShipmentDetails: false, + mayCollectCash: false, + ); + + static const TaskProfile customerPickup = TaskProfile._( + kind: WorkKind.customerPickup, + groupsBySource: false, + leavesHomeOnAccept: true, + deliversToCustomer: false, + endsAtHub: false, + capturesShipmentDetails: true, + mayCollectCash: true, + ); + + static const TaskProfile customerDelivery = TaskProfile._( + kind: WorkKind.customerDelivery, + groupsBySource: false, + leavesHomeOnAccept: true, + deliversToCustomer: true, + endsAtHub: false, + capturesShipmentDetails: false, + mayCollectCash: true, + ); + + static const TaskProfile hubHandover = TaskProfile._( + kind: WorkKind.hubHandover, + groupsBySource: false, + leavesHomeOnAccept: true, + deliversToCustomer: false, + endsAtHub: true, + capturesShipmentDetails: false, + mayCollectCash: false, + ); + + static const TaskProfile done = TaskProfile._( + kind: WorkKind.done, + groupsBySource: false, + leavesHomeOnAccept: true, + deliversToCustomer: false, + endsAtHub: false, + capturesShipmentDetails: false, + mayCollectCash: false, + ); + + /// Which surface this item belongs on. The whole point of the type. + /// + /// • **Home** — assigned, not yet in the rider's hands, and not yet an + /// appointment he has taken on. + /// • **Bookings** — a customer visit he has accepted and still owes. + /// • **Deliveries** — anything already in his custody. + /// • **Activity** — finished. + WorkSurface surfaceWhen({required bool accepted}) => switch (kind) { + WorkKind.done => WorkSurface.activity, + WorkKind.customerPickup => + accepted ? WorkSurface.bookings : WorkSurface.home, + WorkKind.sourcePickup => WorkSurface.home, + WorkKind.customerDelivery || + WorkKind.hubHandover || + WorkKind.unknown => WorkSurface.deliveries, + }; + + /// Resolve a queue row. + /// + /// Order matters: `next_action` is asked first because it is the server's + /// statement about **where this parcel is in its life**, and that outranks + /// where it was collected from. A parcel picked up at a customer's door is a + /// delivery once it is in the bag; it stops being a pickup the moment it is + /// collected, and only the leg knows that. + static TaskProfile of(Map row) { + final leg = NextLegResolver.rowNextAction(row); + + switch (leg) { + case NextActionWord.deliver: + case NextActionWord.startDelivery: + return customerDelivery; + case NextActionWord.inwardAtHub: + return hubHandover; + case NextActionWord.handedToHub: + case NextActionWord.none: + return done; + } + + // Still to be collected — `next_action: pickup`, or a row too old to carry + // one. `stoptype` is the second witness: the server derives it from the + // booking status, so it answers even when next_action is absent. + final stopType = (row['stoptype'] ?? '').toString().trim().toLowerCase(); + if (leg.isEmpty && stopType == 'delivery') { + // Collected, and nothing said where it goes. Custody without a + // destination — NextLeg.unknown's case exactly. + return unknownTask; + } + + return switch (PickupSource.of(row)) { + PickupSource.customer => customerPickup, + PickupSource.hub || + PickupSource.merchant || + PickupSource.store => sourcePickup, + // ── No word from the server: take the safe shape, not a kind ── + // + // This said [sourcePickup] once, and that was the bug. `sourcePickup` + // carries `groupsBySource: true`, so every row too old to carry a + // `pickup_source_type` — which is every fixture in the existing suite, + // and every booking written before these fields shipped — started + // grouping under whatever name it could scrape together. A logistics day + // that is meant to bucket under one assigning base split into a group per + // customer. `hub_pickup_group_test.dart` caught it. + // + // An absent field is not evidence of a kitchen. It is evidence of + // nothing, and [unknownTask] is what nothing looks like: no grouping, no + // door capture, no promise about where it ends. Same doctrine + // [PickupSource] already states — the server's word, or an honest "not + // known", never a guess. + PickupSource.unknown => unknownTask, + }; + } +} + +/// The four places work can live in the rider app. +enum WorkSurface { home, bookings, deliveries, activity } + +/// What a whole trip looks like, derived from the stops actually in it. +/// +/// ── Why a trip needs its own answer ── +/// +/// A few things on Home describe the **day**, not one stop: whether it starts +/// at a kitchen, whether it ends by returning to a base. Those used to read the +/// rider's line, which made them constant for the whole app — true enough when +/// a rider only ever did one kind of work. +/// +/// Under mixed work they are no longer constant, and they are no longer even +/// exclusive: one trip can hold a kitchen load *and* five customer pickups, and +/// can finish by handing some parcels to a base while the last meal goes to +/// somebody's door. So the question changes from "which line is this rider on" +/// to "what is actually in this trip", and the honest answer is a fold over the +/// stops. +/// +/// [endsAtHub] is `any`, not `every`, deliberately: if even one parcel has to +/// be handed in at a base, the rider's day ends at that base. Telling him +/// "END · HOME" while he is still carrying something the network needs is the +/// failure worth avoiding. +abstract final class TripShape { + /// The trip collects in bulk somewhere — so Home can head that group with the + /// place it came from. + static bool groupsBySource(Iterable> stops) => + stops.any((s) => TaskProfile.of(s).groupsBySource); + + /// Something in the trip has to be handed over at a base before the rider is + /// finished. + static bool endsAtHub(Iterable> stops) => + stops.any((s) => TaskProfile.of(s).endsAtHub); + + /// The trip contains at least one customer visit the rider owes — the work + /// that belongs on Bookings rather than Home once accepted. + static bool hasCustomerPickup(Iterable> stops) => + stops.any((s) => TaskProfile.of(s).kind == WorkKind.customerPickup); +} + +/// ───────────────────────────────────────────────────────────────────────── +/// THE BRIDGE — per item where the row knows, per line where it does not +/// +/// [TaskProfile] is the right answer and it could not simply replace +/// [ServiceProfile.active] in one pass, for a reason worth stating plainly +/// rather than discovering twice: **most rows do not carry the discriminator +/// yet.** `pickup_source_type` and `next_action` shipped recently; a booking +/// written before them, and every fixture in the existing suite, carries +/// neither. [TaskProfile.of] correctly answers [WorkKind.unknown] for those, +/// and a screen that took `unknown` at face value would lose the milk round its +/// kitchen headings and the logistics day its door capture — which is what +/// happened both times a grouping site was migrated wholesale. +/// +/// So this is the migration path, not a second source of truth: +/// +/// the row said something → the row decides, per item +/// the row said nothing → the rider's line decides, exactly as before +/// +/// Every method below is that shape. The consequence is the one that matters +/// for mixed work: a CX customer pickup sitting next to a kitchen collection +/// now gets **its own** rules on the strength of its own `pickup_source_type`, +/// instead of inheriting whatever the rider's tenant happens to say. The +/// fallback keeps every existing row behaving precisely as it does today, which +/// is what makes this safe to ship in the same release as the fence. +/// +/// As the backend fills these fields in on every row (see handoff BE-6), the +/// fallbacks stop being reachable and can be deleted a call site at a time. +/// ───────────────────────────────────────────────────────────────────────── +abstract final class WorkPolicy { + /// Does the rider have to establish what is being shipped at this stop — + /// count the parcels, weigh them, capture destination and recipient, take + /// payment? + /// + /// This is the question that most needed taking off the rider. It had exactly + /// one call site, `ServiceProfile.active.needsVerification`, and that static + /// gives the *same answer for every row on the screen*: on a meal tenant a CX + /// customer pickup was walked straight past the door flow — no parcel count, + /// no destination, no weight — and the booking pivoted on whatever the + /// customer had typed into the app days earlier. On a parcel tenant the + /// reverse: a kitchen collection of fourteen known bags asked the rider to + /// itemise a manifest that was already on his screen. + /// + /// A customer pickup needs it. A collection from a counter does not — the + /// manifest is known before he arrives. + static bool capturesShipmentDetails(Map row) { + final profile = TaskProfile.of(row); + if (profile.kind == WorkKind.unknown) { + return ServiceProfile.active.needsVerification; + } + return profile.capturesShipmentDetails; + } + + /// Does this stop establish the shipment's addresses and price — the + /// logistics desk, as against a proof-of-collection photo? + /// + /// Same shape and the same reason. Falls back to + /// `ServiceProfile.active.capturesShipmentAddresses`. + static bool capturesShipmentAddresses(Map row) { + final profile = TaskProfile.of(row); + if (profile.kind == WorkKind.unknown) { + return ServiceProfile.active.capturesShipmentAddresses; + } + return profile.capturesShipmentDetails; + } + + /// Does collecting here bring a shipment into existence? + static bool initiatesShipment(Map row) { + final profile = TaskProfile.of(row); + if (profile.kind == WorkKind.unknown) { + return ServiceProfile.active.initiatesShipment; + } + return profile.kind == WorkKind.customerPickup; + } + + /// Does an accepted stop stay on Home until the rider is actually carrying + /// it, rather than moving to Bookings the moment he accepts? + /// + /// The per-item replacement for `ServiceProfile.active.handsOffAtCollection` + /// (`handoffAt == HandoffPoint.collected`). A **source pickup** stays: taking + /// on a kitchen load puts nothing in his hands and he still has to go and + /// collect it. A **customer pickup** does not: accepting it is an appointment + /// he now owes somebody, and it belongs on Bookings until he has been. + /// + /// Asking the rider's line for this gave one answer for every row on the + /// screen — so on a meal tenant an accepted CX pickup sat on Home beside ten + /// kitchen bags with nothing to distinguish it, and on a parcel tenant a + /// kitchen load vanished off Home the instant it was accepted. + static bool staysOnHomeUntilCollected(Map row) { + final profile = TaskProfile.of(row); + if (profile.kind == WorkKind.unknown) { + return ServiceProfile.active.handsOffAtCollection; + } + return !profile.leavesHomeOnAccept; + } + + /// Which surface this item belongs on. + /// + /// The whole point of [TaskProfile], and the answer the four-tab shell cannot + /// yet act on in full — see the release report. Correct per item today, so + /// the queues can be split without re-deriving the rule. + static WorkSurface surfaceOf( + Map row, { + required bool accepted, + }) => TaskProfile.of(row).surfaceWhen(accepted: accepted); + + /// True when this one item ends by handing over at a base. + /// + /// Read from the row's `next_action`. Unlike the flags above there is no + /// fallback and none is wanted: [TaskProfile.hubHandover] is reached only + /// when the server has said `inward_at_hub`, and guessing a base leg from the + /// rider's line is how a hyperlocal parcel gets routed to a counter. + static bool endsAtHub(Map row) => + TaskProfile.of(row).kind == WorkKind.hubHandover; + + /// True when this one item ends at a receiver's door. + /// + /// Falls back to the line, because a row with no `next_action` genuinely does + /// not say — and on a meal run every stop does end at a door. + static bool deliversToCustomer(Map row) { + final profile = TaskProfile.of(row); + if (profile.kind == WorkKind.unknown) { + return ServiceProfile.active.deliversToCustomer; + } + return profile.deliversToCustomer; + } +} diff --git a/lib/data/work_domain.dart b/lib/data/work_domain.dart index 2f22b36..b6ef141 100644 --- a/lib/data/work_domain.dart +++ b/lib/data/work_domain.dart @@ -87,7 +87,9 @@ abstract final class WorkBoundary { }) { final status = stopStatusOf(stop); if (status.isPicked || status.isDeliveryLeg) return true; - return collectedIds.contains(MilkRun.idOf(stop)); + // Booking key as well as stop key: a multi-destination pickup is collected + // once, before it splits into one row per door. See [MilkRun.wasCollected]. + return MilkRun.wasCollected(stop, collectedIds); } /// True when this order is finished or withdrawn, whatever screen it was on. diff --git a/lib/data/work_scope.dart b/lib/data/work_scope.dart index 4fef863..165fdf7 100644 --- a/lib/data/work_scope.dart +++ b/lib/data/work_scope.dart @@ -42,13 +42,8 @@ import 'package:miler/data/service_profile.dart'; class WorkScope { final int userId; final int tenantId; - final ServiceLine line; - const WorkScope({ - required this.userId, - required this.tenantId, - required this.line, - }); + const WorkScope({required this.userId, required this.tenantId}); /// The scope of the session running right now. /// @@ -65,26 +60,17 @@ class WorkScope { ? raw : int.tryParse(raw?.toString() ?? '') ?? 0; final tenantId = await ApiConfig.storedTenantId(); - return WorkScope( - userId: userId, - tenantId: tenantId, - line: ServiceProfile.active.line, - ); + return WorkScope(userId: userId, tenantId: tenantId); } catch (e) { debugPrint('[SCOPE] could not resolve the session scope: $e'); - return WorkScope( - userId: 0, - tenantId: 0, - line: ServiceProfile.active.line, - ); + return const WorkScope(userId: 0, tenantId: 0); } } /// A signed-out or unreadable session. Deliberately still a real scope /// rather than null: code paths that run before login write to their own /// drawer instead of into the last rider's. - static WorkScope get anonymous => - WorkScope(userId: 0, tenantId: 0, line: ServiceProfile.active.line); + static WorkScope get anonymous => const WorkScope(userId: 0, tenantId: 0); /// The suffix that turns a store key into this scope's key. /// @@ -93,11 +79,36 @@ class WorkScope { /// All three parts are in it because all three can change independently: a /// rider can move tenant, a tenant can run either line, and one device can /// see several riders. - String get key => 'u$userId.t$tenantId.${line.name}'; + String get key => 'u$userId.t$tenantId'; /// Scopes a legacy global key. String scoped(String baseKey) => '$baseKey::$key'; + /// The keys this scope's records used to live under, newest convention first. + /// + /// ── Why the line came out of the key ── + /// + /// The key used to carry a third part, the rider's service line: + /// `completed_bookings::u38.t13.milkMan`. That was right while a rider was + /// one thing for the life of his account. He is not: one Miler carries meal + /// parcels, logistics collections and customer pickups in the same shift, and + /// keying his records by a line splits one day's work across two drawers — + /// with whichever drawer the app resolved into today being the only one he + /// can see. Work he finished an hour ago disappears because a label changed. + /// + /// Rider and tenant still scope it. Those are facts about *who* stored the + /// record, which is what ownership means. The line was a fact about *what he + /// was doing*, which belongs on the work item, not on the drawer. + /// + /// Every historical spelling is listed so a rider upgrading mid-shift keeps + /// what he has already done. + Iterable legacyScopedKeys(String baseKey) sync* { + for (final line in ServiceLine.values) { + yield '$baseKey::u$userId.t$tenantId.${line.name}'; + } + } + + /// Does [record] provably belong to this scope? /// /// Used on rows that were written before scoping existed, and as a @@ -127,12 +138,9 @@ class WorkScope { 'scopeuserid', ]); final rowTenant = intOf(const ['tenantid', 'TenantId', 'scopetenantid']); - final rowLine = (record['scopeline'] ?? '').toString(); - - if (rowUser == 0 && rowTenant == 0 && rowLine.isEmpty) return false; + if (rowUser == 0 && rowTenant == 0) return false; if (rowUser != 0 && rowUser != userId) return false; if (rowTenant != 0 && rowTenant != tenantId) return false; - if (rowLine.isNotEmpty && rowLine != line.name) return false; return true; } @@ -168,11 +176,8 @@ class WorkScope { 'scopeuserid', ]); final rowTenant = intOf(const ['tenantid', 'TenantId', 'scopetenantid']); - final rowLine = (record['scopeline'] ?? '').toString(); - if (rowUser != 0 && userId != 0 && rowUser != userId) return true; if (rowTenant != 0 && tenantId != 0 && rowTenant != tenantId) return true; - if (rowLine.isNotEmpty && rowLine != line.name) return true; return false; } @@ -184,18 +189,16 @@ class WorkScope { ...record, 'scopeuserid': userId, 'scopetenantid': tenantId, - 'scopeline': line.name, }; @override bool operator ==(Object other) => other is WorkScope && other.userId == userId && - other.tenantId == tenantId && - other.line == line; + other.tenantId == tenantId; @override - int get hashCode => Object.hash(userId, tenantId, line); + int get hashCode => Object.hash(userId, tenantId); @override String toString() => 'WorkScope($key)'; diff --git a/lib/main.dart b/lib/main.dart index 2b595a7..ed2e08b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,3 @@ -import 'dart:async' show unawaited; import 'dart:io'; import 'package:miler/helpers/http_overrides.dart'; import 'package:flutter/material.dart'; @@ -22,7 +21,10 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:miler/background/backgroundservice.dart'; import 'package:miler/helpers/shift_end_alarm.dart'; import 'package:miler/data/accepted_store.dart'; +import 'package:miler/data/miler_api.dart'; import 'package:miler/data/service_profile.dart'; +import 'package:miler/data/session.dart'; +import 'package:miler/views/onboardscreens/Sign_in.dart'; import 'package:miler/xpress/delivery_bootstrap.dart'; import 'package:miler/views/helpers/constants/app_theme.dart'; import 'package:miler/views/helpers/widgets/page_transitions.dart'; @@ -77,9 +79,29 @@ Future recheckVersion() async { Future main() async { WidgetsFlutterBinding.ensureInitialized(); - // One-time drain of the pre-scoping global stores. See [migrateLegacyStores] - // for why unattributable rows are dropped rather than adopted. - unawaited(migrateLegacyStores()); + // ── Awaited, because the screens read what it writes ── + // + // This was `unawaited(...)`, which meant the one-time drain of the + // pre-scoping stores raced the first frame. On the launch after an upgrade — + // the only launch where it does anything — Home, Deliveries and Activity + // could all read the scoped key *before* the migration had written it, and a + // rider mid-shift opened the app to an empty day. The rows were not lost; he + // simply could not see them until he killed and reopened the app, which is + // not a thing he knows to do. + // + // It is cheap on every other launch: `containsKey` against an already-loaded + // preferences map, for eight base keys, and it returns before the first + // `await` when there is nothing to move. + // + // Guarded, and deliberately not fatal. A migration that throws — a corrupt + // blob, a prefs backend that will not open — must not stop the rider signing + // in and working. He loses local history he can no longer see anyway; the + // server's record is untouched by any of this. + try { + await migrateLegacyStores(); + } catch (e, st) { + debugPrint('[SCOPE] legacy store migration failed, continuing: $e\n$st'); + } HttpOverrides.global = MyHttpOverrides(); try { @@ -102,6 +124,25 @@ Future main() async { // way to receive it at all. Refreshed from `GET /miler/profile` first so the // resolution below reads the current answer. Cheap and non-blocking for a // rider we already have a name for; see [refreshTenantFromProfile]. + // ── What happens when the server stops accepting this rider ── + // + // `MilerApi` drops the bearer token on any 401 and calls `onUnauthorized`. + // Nothing ever assigned it, so the second half never happened: the credential + // was deleted and the app went on believing it was signed in, because the + // profile and `logged_out = false` were both still on disk. Every call after + // that came back `401 authorization header is required`, forever, behind a + // dashboard showing the rider's own name. + // + // Observed on a handset: rider 23, name and tenant drawn on Account, no + // `authtoken` in SharedPreferences, Home / Deliveries / Activity and the + // heartbeat all failing in a loop. What it costs is not a blank screen — it + // is a rider who sees no jobs, concludes there is no work today, and is never + // told to sign in again. + // + // Registered here, before the line branch below, because both lines make + // authenticated calls and either can be the one to receive the 401. + MilerApi.onUnauthorized = _handleSessionExpired; + await refreshTenantFromProfile(); final profile = await TenantController.to.load(); @@ -140,6 +181,53 @@ Future main() async { runApp(const _RootApp()); } +/// True while a session-expiry teardown is already under way. +/// +/// A 401 rarely arrives alone — the home poll, the deliveries queue and the +/// background heartbeat can each get one within the same second, and every one +/// of them calls this. Without the latch the rider would be torn down three +/// times and pushed at the sign-in screen three times, which on GetX stacks +/// three routes he then has to dismiss. +bool _signingOut = false; + +/// Ends the session the server has stopped accepting, and says so. +/// +/// Deliberately not silent. The rider did not choose this, so he is told what +/// happened in the words of the thing that happened — his session ended — and +/// landed somewhere he can act on it. Dropping him on sign-in with no message +/// reads as the app having crashed and lost his work. +Future _handleSessionExpired() async { + if (_signingOut) return; + _signingOut = true; + try { + await endSession(); + + // A 401 can reach a background isolate, where there is no navigator and + // `Get.offAll` would throw. The teardown above has still happened, so the + // next launch reads `logged_out` and opens sign-in — the rider is not left + // holding a dead session either way. + if (Get.context == null) { + debugPrint('[AUTH] session expired with no UI attached — ' + 'cleared, sign-in will open on next launch'); + return; + } + + Get.offAll(() => const SignIn()); + Get.snackbar( + 'Signed out', + 'Your session ended. Please sign in again to see your jobs.', + snackPosition: SnackPosition.BOTTOM, + ); + } catch (e, st) { + // Never let the teardown itself throw into an API response handler: the + // call that received the 401 has its own error path and this must not + // replace it with a crash. + debugPrint('[AUTH] session teardown failed: $e\n$st'); + } finally { + _signingOut = false; + } +} + /// Paints the status and navigation bars to match the page behind them. void _applySystemChrome() { SystemChrome.setSystemUIOverlayStyle( diff --git a/lib/providers/auth/auth_provider.dart b/lib/providers/auth/auth_provider.dart index b5ef2be..770a24a 100644 --- a/lib/providers/auth/auth_provider.dart +++ b/lib/providers/auth/auth_provider.dart @@ -93,6 +93,11 @@ class AuthProvider { required String fcmToken, int? pin, String? pinRaw, + + /// True for a rider setting his PIN for the first time — routes to + /// `POST /miler/set-pin` and sends the digits as `new_pin`. The response, + /// the token handling and the persisted identity are identical either way. + bool firstTime = false, }) async { // The legacy `rider/login` path on the retired backend that used to sit // behind a flag here is gone with the rest of the old backend. @@ -101,6 +106,8 @@ class AuthProvider { pin: pin, pinRaw: pinRaw, fcmToken: fcmToken, + path: firstTime ? '/miler/set-pin' : '/miler/verify-pin', + pinField: firstTime ? 'new_pin' : 'pin', ); } @@ -117,13 +124,27 @@ class AuthProvider { /// AssignMilerToBooking pushes "New Pickup Assigned" to exactly that token. /// Omitting it leaves the profile's token empty and silently makes the app /// poll-only. + /// ── One normaliser, two routes ── + /// + /// `POST /miler/set-pin` answers with the **same envelope** as verify-pin — + /// `{success, token, tenantid, tenantname, user:{authname, contactno, + /// profile:{…}}}` — because it is also a sign-in: a rider who sets his PIN is + /// logged in by that one call and does not verify afterwards. + /// + /// So the path and the PIN field name are arguments rather than a second copy + /// of this function. Everything below — finding the token in any of the nine + /// places this contract has worn it, splitting the display name, building the + /// legacy `{status, details:{…}}` envelope that [loginParsed] persists from — + /// is the part that must not drift between the two paths, and now cannot. Future _loginNew({ required String contactNo, int? pin, String? pinRaw, String? fcmToken, + String path = '/miler/verify-pin', + String pinField = 'pin', }) async { - final uri = Uri.parse(ApiConfig.url('/miler/verify-pin')); + final uri = Uri.parse(ApiConfig.url(path)); // The backend bcrypt-compares the PIN as a STRING, so a leading zero is // significant. Prefer the raw text the rider typed — round-tripping through // int drops it ("0512" -> 512 -> "512") and fails a valid PIN. @@ -132,7 +153,7 @@ class AuthProvider { : pin?.toString(); final body = { 'phone': contactNo, - if (pinValue != null) 'pin': pinValue, + if (pinValue != null) pinField: pinValue, // Riders live in partition 1001. It defaults server-side, so omitting it // appeared to work — but a miler row created without it can never log in, // and sending it explicitly is the only way the app and the console agree @@ -168,7 +189,7 @@ class AuthProvider { // Signing in with nothing to sign in to. This is the one call that cannot // be intercepted in [MilerApi] — auth posts its own request, because it // runs before there is a token for the shared transport to attach. - final mocked = MockBackend.respond('POST', '/miler/verify-pin', body: body); + final mocked = MockBackend.respond('POST', path, body: body); final http.Response res = mocked != null ? http.Response( @@ -451,6 +472,7 @@ class AuthProvider { required String fcmToken, int? pin, String? pinRaw, + bool firstTime = false, }) async { final res = await login( contactNo: contactNo, @@ -460,6 +482,7 @@ class AuthProvider { fcmToken: fcmToken, pin: pin, pinRaw: pinRaw, + firstTime: firstTime, ); final Map jsonMap = res.body.isNotEmpty ? json.decode(res.body) as Map @@ -608,51 +631,39 @@ class AuthProvider { return Login.fromJson(jsonMap); } + /// Deprecated. `POST /miler/reset-pin` is admin-only and this app must never + /// call it — it was once open, and reset-pin followed by verify-pin took over + /// any rider account given nothing but a phone number. + /// + /// ── The 403 this used to manufacture is gone ── + /// + /// There was no rider-facing PIN write, so this returned a synthetic + /// `403 "Your MPIN is issued by your office and cannot be changed from the + /// app."` — true when written, and a dead end for the Create-MPIN screen. + /// + /// `POST /miler/set-pin` is that route, shipped 2026-09-16. It is + /// self-service, cannot overwrite an existing PIN (409), and returns a full + /// session. Riders set their own PIN on first sign-in and the console no + /// longer issues one, so nothing needs this method any more. + @Deprecated('Use AuthController.setPin, which calls POST /miler/set-pin.') Future updatePin({ required int userId, required int pin, }) async { - // ── There is no rider-facing set-PIN endpoint, and that is deliberate ── - // - // The only PIN-write route on the backend is `POST /miler/reset-pin`, and - // it requires an ADMIN token. It was once open, and reset-pin followed by - // verify-pin took over any rider account given nothing but a phone number. - // The app must not call it; rider PIN resets go through ops. - // - // So this stays a no-op success: the Create-MPIN screen's flow completes - // and the PIN the account was issued with remains the one that works. - // Making it fail instead would strand a rider on a screen with no way - // forward, which is worse and no more honest. - // ── It used to answer 200 ── - // - // "Making it fail instead would strand a rider on a screen with no way - // forward, which is worse and no more honest." Half of that was right and - // the conclusion was wrong. What the manufactured 200 actually did: - // - // 1. Create-MPIN told the rider his new MPIN was saved. - // 2. The app wrote it to `dbPin` locally. - // 3. The server never heard about it. - // 4. Every login from then on returned `incorrect PIN`, forever, with no - // way for the rider to tell that the PIN he was typing had never - // existed anywhere but his own handset. - // - // Being stranded on a screen that tells you who can help is not worse than - // that. It is the only version of this that a rider can act on. ApiConfig.logGap( 'updatePin', - 'No rider-facing set-PIN route; reset-pin is admin-only by design. ' - 'Refusing rather than reporting a write that did not happen.', + 'reset-pin is admin-only; first-time PIN creation goes through ' + 'POST /miler/set-pin. This method should have no callers.', ); return http.Response( json.encode({ 'status': false, - 'code': 403, + 'code': 410, 'message': - 'Your MPIN is issued by your office and cannot be changed from the ' - 'app. Ask your supervisor to reset it, then sign in with the MPIN ' - 'they give you.', + 'This app no longer changes PINs through reset-pin. Set your PIN ' + 'on the sign-in screen.', }), - 403, + 410, headers: {'content-type': 'application/json'}, ); } diff --git a/lib/providers/pickuplog/pickuplog_provider.dart b/lib/providers/pickuplog/pickuplog_provider.dart index 98988da..8986ed7 100644 --- a/lib/providers/pickuplog/pickuplog_provider.dart +++ b/lib/providers/pickuplog/pickuplog_provider.dart @@ -240,6 +240,48 @@ class UpdatePickupProvider { // consignment id, and pressing **Delivered** at the door was refused by // the app itself. Recorded here, at the only moment it is guaranteed to // be in hand. See [rememberConsignmentId]. + // ── What the rider captured, and what the contract can carry ── + // + // `POST /miler/bookings/:id/pickup-complete` takes **latitude and + // longitude and nothing else**. The door flow, meanwhile, *demands* a + // photograph of the parcels and — on a short count or a broken seal — + // a written explanation before it will let the rider confirm + // (`StopVerificationPage`, `_needsPickupNote`). + // + // So the app was compelling a rider to produce exactly the evidence + // that matters in a dispute, uploading the photo to object storage, and + // then dropping the URL and the note on the floor: this method reads + // `dropimage` for the delivery leg and has never read `pickupimage`, + // `proofimage` or `notes` on this one. Nobody at the hub could see any + // of it, and nobody knew it was missing. + // + // Two things change here, and neither of them invents a field: + // + // * the photo and the note are kept **on the device**, against the + // order, so the record exists and the office can ask for it — see + // `ProofStore` and the completed-stop record; + // * the gap is *logged*, every time, with what was dropped. It is the + // same channel every other missing route reports through, so it + // shows up where the rest of the contract gaps do instead of being + // a thing one person remembers. + // + // BACKEND DEPENDENCY: see handoff BE-2. When `pickup-complete` accepts + // `pickupimageurl` / `notes` / `condition`, send them here and delete + // this block. + final droppedProof = (data['pickupimage'] ?? data['proofimage'] ?? '') + .toString() + .trim(); + if (droppedProof.isNotEmpty || notes.trim().isNotEmpty) { + ApiConfig.logGap( + 'pickup-complete', + 'The rider captured evidence this route cannot carry — ' + 'photo=${droppedProof.isNotEmpty ? 'yes' : 'no'}, ' + 'note=${notes.trim().isNotEmpty ? '"${notes.trim()}"' : 'none'}. ' + 'pickup-complete accepts latitude/longitude only. Held on the ' + 'device against booking $id. See BE-2.', + ); + } + final picked = await MilerApi.pickupComplete(id, lat: lat, lon: lon); // ── Which lifecycle the server is running, read from the server ── // @@ -566,7 +608,37 @@ class UpdatePickupProvider { // and it keeps the chargeable total correct even though the per-parcel // figures are an even split rather than a measurement. final double each = totalWeight / count; - final parcels = [for (var i = 0; i < count; i++) ParcelEntry(weight: each)]; + + // ── The photographs, at last ── + // + // `photos` has been on this contract since the route shipped and the app + // has never sent it. The keys arrive on the verification map (see + // `_withParcelPhotos`); an empty list means the upload failed, and the + // field is then omitted rather than sent empty — an empty array is a claim + // that nobody photographed anything. + // + // Attached to the FIRST parcel only. The rider takes one photograph of the + // load, not one per box — repeating the same key on every parcel would + // report N photographs where one was taken. + final photoKeys = [ + for (final k in (pickup['photos'] as List?) ?? const []) + if (k.toString().trim().isNotEmpty) k.toString().trim(), + ]; + + final parcels = [ + for (var i = 0; i < count; i++) + ParcelEntry( + weight: each, + photos: i == 0 ? photoKeys : const [], + ), + ]; + if (photoKeys.isEmpty) { + ApiConfig.logGap( + 'submitParcels', + 'booking $bookingId: no photo key available — the parcels are being ' + 'recorded without the door photograph.', + ); + } final res = await MilerApi.submitParcels(bookingId, parcels); debugPrint( diff --git a/lib/views/Dashboard/home/homepage.dart b/lib/views/Dashboard/home/homepage.dart index d7677e3..f8f528d 100644 --- a/lib/views/Dashboard/home/homepage.dart +++ b/lib/views/Dashboard/home/homepage.dart @@ -67,6 +67,7 @@ import 'package:miler/data/order_manifest.dart'; import 'package:miler/data/milk_run.dart'; import 'package:miler/data/order_events.dart'; import 'package:miler/data/work_domain.dart'; +import 'package:miler/data/geofence.dart'; import 'package:miler/data/work_repository.dart'; import 'package:miler/data/pickup_locations.dart'; import 'package:miler/data/service_day.dart'; @@ -600,55 +601,67 @@ class _HomepageState extends State } // ==================== PROXIMITY METHODS ==================== - Future _isNearPickupLocation(Map Booking) async { - // ── The second proximity gate ── - // - // `PickupsController._checkGeofence` guards a single stop. This one guards - // the *bulk* "mark selected as arrived" action on Home, and it runs before - // the controller is ever called. - // - // It honours the same switch and, since this change, the same **radius**. - // It was hardcoded to 500 m while the controller read a configured 100 — - // so the app had two fences of different sizes on two routes into the same - // rung, and which one a rider met depended on whether he had ticked boxes - // or slid a sheet. One number now: [kGeofenceRadiusMeters]. - if (kBypassGeofenceForTesting) { - debugPrint( - '[GEOFENCE] OFF for bulk arrival — enforcement is disabled in this ' - 'build. Stop completion is NOT proximity-verified. ' - 'See kGeofenceEnforced.', - ); - return true; - } - try { - final pickupLat = _parseDouble( - Booking['pickuplat'] ?? Booking['PickupLat'], - ); - final pickupLng = _parseDouble( - Booking['pickuplon'] ?? Booking['PickupLon'], - ); - if (pickupLat == 0 || pickupLng == 0) return true; - final riderLoc = await _getValidCoordinates(); - if (riderLoc == null) return true; - final double riderLat = double.tryParse(riderLoc.$1) ?? 0; - final double riderLng = double.tryParse(riderLoc.$2) ?? 0; - if (riderLat == 0 || riderLng == 0) return true; - final double distanceInMeters = Geolocator.distanceBetween( - riderLat, - riderLng, - pickupLat, - pickupLng, - ); - // The phone's own error is credited to the rider here too — see - // [kGeofenceRadiusMeters] for why a 10 m fence measured with a ±20 m fix - // has to, and `_getValidCoordinates` for the fix it is measured with. - final double slack = _lastFixAccuracy; - return (distanceInMeters - slack) <= kGeofenceRadiusMeters; - } catch (e) { - debugPrint('[PROXIMITY] Error checking proximity: $e'); - return true; - } + /// The fence's answer for the last stop [_isNearPickupLocation] measured. + /// + /// Kept so the bulk-arrival sheet can name a distance instead of repeating a + /// generic "you are too far" for every ticked box. + GeofenceDecision? _lastBulkGeofence; + + /// The bulk gate on Home: the *selected stops* route into the arrived rung. + /// + /// ── One fence, reached two ways ── + /// + /// `PickupsController._checkGeofence` guards a single stop; this guards the + /// "mark selected as arrived" action, and it runs before the controller is + /// ever called. The two used to be separate implementations with separate + /// radii — 500 m hardcoded here against the controller's configured 100 — so + /// which fence a rider met depended on whether he ticked boxes or slid a + /// sheet. Both now call [Geofence] and there is no arithmetic left in this + /// file to drift. + /// + /// ── It used to fail open in four places ── + /// + /// A booking with no pin, a rider fix that could not be read, a zero + /// coordinate pair and any thrown exception each ended in `return true`. All + /// four are refusals now; [Geofence] decides which sentence the rider sees. + Future _isNearPickupLocation(Map booking) async { + final decision = await Geofence.check( + targetLat: _parseDouble(booking['pickuplat'] ?? booking['PickupLat']), + targetLng: _parseDouble(booking['pickuplon'] ?? booking['PickupLon']), + action: 'Arrived', + ); + _lastBulkGeofence = decision; + return decision.allowed; + } + + /// How many bulk status writes may be in flight at once. + /// + /// Not unbounded. Twenty simultaneous requests from one handset queue at the + /// radio on a rider's 3G connection and finish *slower* than a handful, and a + /// thundering herd from one rider is a poor shape to hand the backend. Six is + /// enough to hide the per-request latency — the thing that made the old + /// sequential loop take a minute — without any of that. + static const int _bulkLanes = 6; + + /// Straight-line kilometres from the rider to a stop, measured on the device. + /// + /// Handed to `updatePickedStatus` as `actualKms` purely so its priority-3 + /// branch — an OSRM road-distance request per order, six-second timeout — + /// never runs during a bulk action. It is the same straight line that branch + /// already falls back to when the routing service fails. + double _bulkLegKm( + String riderLat, + String riderLng, + String stopLat, + String stopLng, + ) { + final rLat = double.tryParse(riderLat) ?? 0; + final rLng = double.tryParse(riderLng) ?? 0; + final sLat = double.tryParse(stopLat) ?? 0; + final sLng = double.tryParse(stopLng) ?? 0; + if (rLat == 0 || rLng == 0 || sLat == 0 || sLng == 0) return 0; + return Geolocator.distanceBetween(rLat, rLng, sLat, sLng) / 1000.0; } Future> _checkProximityForOrders( @@ -695,7 +708,7 @@ class _HomepageState extends State content: Text( specificMessage ?? 'You must be within ' - '${kGeofenceRadiusMeters.toStringAsFixed(0)} metres of the ' + '${kGeofenceRadiusMetres.toStringAsFixed(0)} metres of the ' 'pickup location to mark this booking as arrived.', style: TextStyle( fontSize: FontConstants.regular(context), @@ -2054,6 +2067,34 @@ class _HomepageState extends State // is slide again at a stop that will keep refusing. See // [PickupsController.lastPickupRefusal]. if (nextStatus == 'PICKED') return dc.lastPickupRefusal ?? 'refused'; + + // ── Optimistic covers a write that did not LAND, never a rider who is + // not THERE ── + // + // The paragraph above is right about the case it describes: a rider at + // a counter with no signal must not have his own report of where he is + // snapped back by a late poll. + // + // But `updateArrivedStatus` answers false for three different things, + // and only one of them is that case. It is also false when the geofence + // refused — the rider is 1.4km from the door and no request was ever + // made — and when the server itself rejected the arrival on a business + // rule. Advancing on those wrote ARRIVED onto a stop the rider had not + // reached, and the admin console, which only ever hears what the server + // was told, went on showing the stop as pending. That is the divergence + // reported from the field: arrived on the phone, not arrived in the + // office, and no way for either side to tell who was wrong. + // + // So the two "he is not there / the office said no" cases stop here and + // are handed back to be shown. A dead network still falls through and + // still advances — with [PickupsController.lastArrivalNotice] telling + // him the office has not been told. + // + // `lastBlockedReason` is read first and is always fresh: `_checkGeofence` + // clears it on entry, so a non-null value can only have come from the + // check that just ran. + final notThere = dc.lastBlockedReason ?? dc.lastArrivalRefusal; + if (notThere != null) return notThere; } // ── The rung the SERVER produced, not the one the button is named after ── @@ -2930,13 +2971,22 @@ class _HomepageState extends State } if (targetStatus == 'ARRIVED') { - final proximityResults = await _checkProximityForOrders(selectedOrderIds) - .timeout( - const Duration(seconds: 3), - onTimeout: () => Map.fromEntries( - selectedOrderIds.map((id) => MapEntry(id, true)), - ), - ); + // ── The 3-second fail-OPEN is gone ── + // + // This was wrapped in `.timeout(3s, onTimeout: () => everything inside + // the fence)`. Each check took its own 8-second GPS fix, so twenty of + // them could never finish in three seconds — the timeout fired on + // essentially every bulk arrival and waved every order through whatever + // the distance was. A hole straight through the 100 m rule, and one that + // looked like a safety net. + // + // It is not needed now: [Geofence] reuses one live fix for + // [kGeofenceFixReuse], so twenty checks cost one GPS settle and complete + // in milliseconds. If the fix itself cannot be had, each check refuses on + // its own terms and says which of location-off / permission / no-signal + // it was — which is the answer the rider can act on, and the one a + // blanket "allow everything" was hiding. + final proximityResults = await _checkProximityForOrders(selectedOrderIds); final farOrders = proximityResults.entries .where((e) => !e.value) @@ -2948,7 +2998,8 @@ class _HomepageState extends State await _showProximityWarning( context, specificMessage: farOrders.length == selectedOrderIds.length - ? 'You must be within 500 meters of the pickup location(s) to mark bookings as arrived.' + ? 'You must be within ${kGeofenceRadiusMetres.round()} m of the pickup ' + 'location to mark it arrived.' : 'Some selected bookings are too far from their pickup locations.', ); } @@ -3041,11 +3092,41 @@ class _HomepageState extends State /// than at the next poll. final Set bulkCollected = {}; - for (final orderId in selectedOrderIds) { + // ── Phase 1: the network, overlapped ── + // + // This was a plain sequential `for`, so twenty orders meant twenty full + // round trips end to end — status call, GPS fix, routing call and four + // preferences writes each, nothing overlapping anything. Measured at + // 3–5 s per order, which is the 1–2 minutes riders were reporting for a + // twenty-bag counter pick. + // + // The writes are independent — different bookings, different + // [MutationGuard] keys (`picked:1042`), and the backend carries an + // `Idempotency-Key` per action — so they can be in flight together. The + // *local* bookkeeping is NOT done here: it is collected and applied once, + // below, so the order of local state changes stays deterministic and a + // growing JSON blob is not rewritten twenty times. + // + // Bounded to [_bulkLanes] rather than `Future.wait` over everything: + // twenty simultaneous requests on a rider's 3G connection queue at the + // radio and finish slower than six, and it is a kinder shape for the + // backend than a thundering herd from one handset. + final List bulkIds = []; + final Map> bulkStops = {}; + for (final id in selectedOrderIds) { // Not `_ordersMap` — a stop past ACCEPT is no longer in it, and every // rung after the first was silently skipped here. See [_bulkStopFor]. - final Booking = _bulkStopFor(orderId); - if (Booking == null) continue; + final stop = _bulkStopFor(id); + if (stop == null) continue; + bulkIds.add(id); + bulkStops[id] = stop; + } + + /// Which orders the backend confirmed. + final Set bulkOk = {}; + + Future runOne(String orderId) async { + final Booking = bulkStops[orderId]!; final pickupId = int.tryParse('${Booking['pickupid'] ?? 0}') ?? 0; final orderHeaderId = @@ -3085,10 +3166,7 @@ class _HomepageState extends State // Home, still selectable, and pressing again costs a tap. So it // records what the backend confirmed and says what it did not — // the same rule `_advanceStop` already applies to PICKED. - if (success) { - _hiddenOrderIds.add(orderId); - acceptedBookings.add(Booking); - } else { + if (!success) { debugPrint( '[BULK] accept REFUSED for $orderId — leaving it on Home', ); @@ -3110,10 +3188,7 @@ class _HomepageState extends State // successful bulk arrival was a network call, a success count and // a row that came straight back on ACCEPTED — which is what "mark // as arrived does nothing" looked like from the outside. - if (success) { - Booking['orderstatus'] = 'arrived'; - await addArrivedOrderIds([orderId]); - } + if (success) Booking['orderstatus'] = 'arrived'; } else if (targetStatus == 'PICKED') { debugPrint('[BULK] Picking Booking $orderId'); success = await dc.updatePickedStatus( @@ -3125,11 +3200,31 @@ class _HomepageState extends State ridersLng: bulkRidersLng, pickupLat: pickupLat, pickupLng: pickupLng, + // ── Pre-computed, so the routing call never fires ── + // + // Left at 0 this falls through to priority 3, which is an OSRM + // road-distance request per order with a 6-second timeout. On a + // bulk pick the rider never "started" each stop individually, so + // the cumulative-tracking key is absent for most of them and that + // request ran for nearly all twenty — to compute a number that + // `UpdatePickupProvider` never reads and `pickup-complete` never + // sends. The backend derives earnings from the coordinates by its + // own haversine; this figure only feeds Activity's local + // "km ridden". + // + // So it is measured here, on the device, for free — the same + // straight line the priority-3 branch already falls back to when + // OSRM fails. + actualKms: _bulkLegKm( + bulkRidersLat, + bulkRidersLng, + pickupLat, + pickupLng, + ), proofImage: bulkProofImageUrl, ); if (success) { _startPickupPosting(Booking); - _hiddenOrderIds.add(orderId); // ── The write that says the load is in his hands ── // @@ -3146,11 +3241,8 @@ class _HomepageState extends State // time are wrong" half of it. // // Recorded only after the backend confirmed, same rule as accept. - await addCollectedOrderIds([orderId]); + // Applied in phase 2 — see [bulkCollected]. bulkCollected.add(orderId); - // One copy of the day, and it is now stale — Deliveries must not - // render this order from a response that predates the hand-over. - unawaited(WorkRepository.instance.invalidate()); // ── Picked is a hand-over on one line and a finish on the other ── // @@ -3163,14 +3255,8 @@ class _HomepageState extends State // drops he makes himself — so filing it as completed sent it to // Activity instead of to Deliveries, and the delivery leg had // nothing to work. See [ServiceProfile.handsOffAtCollection]. - if (ServiceProfile.active.handsOffAtCollection) { - await addAcceptedBookings([Booking]); - } else { - await removeAcceptedBookings([orderId]); - await addCompletedBookings([Booking]); - } - // Off the arrived rung — it has been collected. - await removeArrivedOrderIds([orderId]); + // The handsOffAtCollection split is applied in phase 2, once, + // against the whole batch. } } } catch (e) { @@ -3179,12 +3265,78 @@ class _HomepageState extends State } if (success) { - successCount++; + bulkOk.add(orderId); } else { debugPrint('[BULK] Failed to update Booking $orderId'); } } + // Bounded-concurrency drain. `next++` is safe: there is no `await` + // between reading and incrementing it, and Dart does not preempt inside a + // synchronous run of statements. + var next = 0; + final int lanes = _bulkLanes < bulkIds.length + ? _bulkLanes + : (bulkIds.isEmpty ? 1 : bulkIds.length); + await Future.wait( + List.generate(lanes, (_) async { + while (true) { + final i = next++; + if (i >= bulkIds.length) break; + await runOne(bulkIds[i]); + } + }), + ); + successCount += bulkOk.length; + + // ── Phase 2: every local store written once ── + // + // Each of these helpers decodes a JSON blob, mutates it and writes it + // back. Called per order inside the loop that was O(n²) writes against a + // growing list, and it left a window where a rider backgrounding the app + // mid-batch had half a batch filed. + final List okIds = bulkIds.where(bulkOk.contains).toList(); + final List> okStops = [ + for (final id in okIds) bulkStops[id]!, + ]; + + if (okIds.isNotEmpty) { + // ── ARRIVED is deliberately NOT hidden ── + // + // Accept and Picked take the row off Home; arriving does not. An + // arrived stop is still work in front of the rider and stays on the + // list — see `arrived_stays_on_home_test.dart`. Hiding all three here + // would have made a bulk arrival look like the stops had vanished. + if (targetStatus == 'ACCEPTED') { + _hiddenOrderIds.addAll(okIds); + acceptedBookings.addAll(okStops); + } else if (targetStatus == 'ARRIVED') { + await addArrivedOrderIds(okIds); + } else if (targetStatus == 'PICKED') { + _hiddenOrderIds.addAll(okIds); + await addCollectedOrderIds(okIds); + // ── Picked is a hand-over on one line and a finish on the other ── + // + // Right for **logistics**, where what he collects goes to the base + // and the booking's own story ends at the counter. On a **milk run** + // collection is the *middle* of the day — every bag is followed by a + // round of drops he makes himself — so filing it as completed would + // send it to Activity instead of Deliveries and the delivery leg + // would have nothing to work. See [ServiceProfile.handsOffAtCollection]. + if (ServiceProfile.active.handsOffAtCollection) { + await addAcceptedBookings(okStops); + } else { + await removeAcceptedBookings(okIds); + await addCompletedBookings(okStops); + } + // Off the arrived rung — it has been collected. + await removeArrivedOrderIds(okIds); + // One copy of the day, and it is now stale — Deliveries must not + // render these orders from a response that predates the hand-over. + unawaited(WorkRepository.instance.invalidate()); + } + } + // The leg every card reads is derived from this set, so it has to land // before the next build — not at the next poll three seconds later. if (bulkCollected.isNotEmpty && mounted) { diff --git a/lib/views/Dashboard/home/stop_card.dart b/lib/views/Dashboard/home/stop_card.dart index 8e48cce..28d58b2 100644 --- a/lib/views/Dashboard/home/stop_card.dart +++ b/lib/views/Dashboard/home/stop_card.dart @@ -3,6 +3,7 @@ import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:miler/data/service_profile.dart'; +import 'package:miler/data/task_profile.dart'; import 'package:miler/views/Dashboard/home/trip.dart'; import 'package:miler/views/Dashboard/pickups/route_metrics.dart'; import 'package:miler/views/Dashboard/pickups/stop_type.dart'; @@ -164,7 +165,8 @@ class StopCard extends StatelessWidget { // he is riding to. After it, the drop leads — and that card lives on the // work tab, where [PickupCard] does exactly the same thing in the other // direction. - final twoLeg = ServiceProfile.active.deliversToCustomer && drop.isNotEmpty; + // Per row: a CX pickup on a meal tenant is not a two-leg meal stop. + final twoLeg = WorkPolicy.deliversToCustomer(stop) && drop.isNotEmpty; final grouped = groupedUnderSource && source.isNotEmpty; final headline = (twoLeg && !grouped && source.isNotEmpty) ? source @@ -278,7 +280,7 @@ class StopCard extends StatelessWidget { final chip = _live ? LiveMark(label: state == StopState.arrived ? 'Arrived' : 'Active') : (state == StopState.pending && - !ServiceProfile.active.handsOffAtCollection) + !WorkPolicy.staysOnHomeUntilCollected(stop)) ? null : StopStateChip(state: state, filled: true); @@ -410,7 +412,7 @@ class StopCard extends StatelessWidget { // On a meal run the parcel counts are the one-label rule restated // as an item count, which is exactly the second number this app // does not allow. Only the money survives. - countsHidden: ServiceProfile.active.deliversToCustomer, + countsHidden: WorkPolicy.deliversToCustomer(stop), ); final showLabel = !labelShownAbove && printed.isNotEmpty; if (!showLabel && meta.isEmpty) return const SizedBox.shrink(); diff --git a/lib/views/Dashboard/home/trip_card.dart b/lib/views/Dashboard/home/trip_card.dart index d230c05..5b5a138 100644 --- a/lib/views/Dashboard/home/trip_card.dart +++ b/lib/views/Dashboard/home/trip_card.dart @@ -21,6 +21,7 @@ import 'package:miler/data/stop_area.dart'; import 'package:miler/data/milk_run.dart'; import 'package:miler/data/pickup_locations.dart'; import 'package:miler/data/service_profile.dart'; +import 'package:miler/data/task_profile.dart'; /// ───────────────────────────────────────────────────────────────────────── /// TRIP CARD — one slot's whole route, start to finish. @@ -694,7 +695,28 @@ class TripCard extends StatelessWidget { // give. `trip_brief_layout_test.dart` sweeps for exactly this. Flexible( child: Text( - ServiceProfile.active.endsAtHub ? 'RETURN · BASE' : 'END · HOME', + // ── Line OR row, and the OR is the point ── + // + // This was migrated to `TripShape.endsAtHub(trip.stops)` alone and + // reverted within the hour, for the reason `task_profile.dart` + // already documents about `sourceIsKitchen`: **the rows cannot + // answer this one.** A base leg is only knowable from + // `next_action: inward_at_hub`, which a parcel does not carry until + // it has been collected — so a logistics trip of five *pre-pickup* + // stops folds to `false` and a rider who has always ended his day + // at a base was told "END · HOME". `two_lines_test` and + // `card_density_test` both caught it. + // + // The line knows where the day ends; it is a fact about the rider's + // round, not about any one parcel. The fold is kept beside it + // because it can only ever *add* — a meal rider carrying one + // hub-routed parcel now correctly reads RETURN · BASE, which + // neither source could say on its own. `any`, not `every`: one + // parcel the network needs is enough to send him to a base. + (ServiceProfile.active.endsAtHub || + TripShape.endsAtHub(trip.stops)) + ? 'RETURN · BASE' + : 'END · HOME', maxLines: 1, overflow: TextOverflow.ellipsis, style: MilerType.eyebrow, @@ -1073,7 +1095,8 @@ class TripCard extends StatelessWidget { // logistics rider genuinely does return collected // shipments to the depot, so the label follows the // capability rather than the screen position. - : (ServiceProfile.active.endsAtHub + : ((ServiceProfile.active.endsAtHub || + TripShape.endsAtHub(trip.stops)) ? 'RETURN · BASE' : 'END · HOME'), maxLines: 1, diff --git a/lib/views/Dashboard/pickups/card.dart b/lib/views/Dashboard/pickups/card.dart index 898f1fe..f20852f 100644 --- a/lib/views/Dashboard/pickups/card.dart +++ b/lib/views/Dashboard/pickups/card.dart @@ -136,7 +136,6 @@ class PickupCard extends StatelessWidget { ], 'Address not available'); final String orderId = _val(['orderid']); final String notes = _val(['notes', 'Notes']); - final String phone = _val(['pickupcontactno']); final int deliverQty = deliveryParcelCount(item); final int collectQty = pickupParcelCount(item); @@ -157,15 +156,54 @@ class PickupCard extends StatelessWidget { // The receiver names the far end whenever the rider is carrying it there — // whether that is a milk round (line-level) or a hyperlocal parcel the // backend routed direct (per-order). + // Asked of the ROW first. On a meal tenant this static answered true for + // every card on the screen, so a CX customer pickup sitting next to a + // kitchen load was drawn as though the rider were already carrying it to a + // receiver. [WorkPolicy] reads the row's own `next_action` / + // `pickup_source_type` and only falls back to the line where the row is + // silent — which keeps every existing payload rendering exactly as it does + // today. final bool carryingToCustomer = - ServiceProfile.active.deliversToCustomer || leg.isCustomer; + WorkPolicy.deliversToCustomer(item) || leg.isCustomer; final bool pickupIsSource = carryingToCustomer && source.isNotEmpty; final String pickupName = pickupIsSource ? source : customer; final String dropAddress = _val(['dropaddress', 'DropAddress']); - final String dropName = pickupIsSource + // ── Who is actually at the far end ── + // + // `pickupcustomer` is the booking's customer, which on a parcel booking is + // the **sender** — the person the rider collected FROM. Using it to name the + // drop put the sender's name over the receiver's door, and on a + // multi-destination pickup it put the same name over all three of them. + // The receiver is carried per destination and is the honest answer whenever + // the payload has one. + final String recipient = _val(['recipientname']); + final String dropName = recipient.isNotEmpty + ? recipient + : pickupIsSource ? customer : (dropAddress.isEmpty ? '' : 'Collection centre'); + // ── The Call button has to dial the door he is standing at ── + // + // `pickupcontactno` is the booking's customer — the SENDER — and it was + // what this button dialled on every card, delivery legs included. So a + // rider at the receiver's gate pressed Call and rang the man he had + // collected from that morning, in another city; on a three-drop pickup he + // rang him at all three gates. The receiver's own number is carried per + // destination and is the right one to ring whenever the rider is on his way + // to that receiver — the same test the name above uses, so the card cannot + // show one person and phone another. + final String recipientPhone = _val(['recipientphone']); + final String phone = (recipient.isNotEmpty && recipientPhone.isNotEmpty) + ? recipientPhone + : _val(['pickupcontactno']); + + /// `Stop 2 of 3` on a customer pickup with several doors, `''` otherwise. + /// Sits with the leg eyebrow because it answers the same question — what + /// kind of stop is this — and because three cards that differ only in their + /// address are three chances to deliver the wrong bag. + final String stopLabel = MilkRun.stopLabel(item); + /// True once the rider is carrying it: the drop becomes the destination and /// takes the top of the card. // ── The immediate destination leads; never a hub leg's receiver ── @@ -179,6 +217,46 @@ class PickupCard extends StatelessWidget { (pickupIsSource || leg.isCustomer) && (dropName.isNotEmpty || dropAddress.isNotEmpty); + // ── On a base leg the BASE leads ── + // + // The card was right to refuse the receiver — he is in another district and + // leading with him is the navigation mistake the rule above exists to + // prevent. But refusing the receiver only left `pickupName`, so a handover + // card read: + // + // BASE HANDOVER + // Anitha R + // Gandhipuram + // + // …the customer he collected from an hour ago, under a heading telling him + // to go to a base. The place he is actually going appeared nowhere on the + // card and only on the sheet's slider, after he had already set off. On a + // round with two bases that is a parcel left in the wrong building. + // + // `next_hub` rides on the row (the backend sends all six fields), so the + // destination is known here. A base with no coordinates still leads with + // its name — it is a caption rather than a destination, and the fence + // refuses the handover anyway with a sentence pointing at the office. + final HandoverHub? handoverBase = leg.isHub + ? (HandoverHub.from(item['next_hub'] ?? item['nexthub']) ?? + PickupLocations.baseFor( + item['next_hub_id'] ?? item['nexthubid'] ?? item['hubid'], + )) + : null; + final bool baseLeads = handoverBase != null; + + /// What the card is headed with, in one place so the name and the address + /// below it can never describe two different places. + final String headlineName = baseLeads + ? handoverBase.name + : (dropLeads ? dropName : pickupName); + final String headlineAddress = baseLeads + ? [ + handoverBase.address, + handoverBase.pincode, + ].where((v) => v.trim().isNotEmpty).join(', ') + : ''; + // ── No accent stripe any more ── // // The card carried a 4px coloured bar down its left edge, on the argument @@ -261,9 +339,15 @@ class PickupCard extends StatelessWidget { // has to say which before the rider reads the place. // Drawn only once the parcel is actually his; an // uncollected stop has no leg yet. - if (leg.leg.eyebrow.isNotEmpty) ...[ + if (leg.leg.eyebrow.isNotEmpty || + stopLabel.isNotEmpty) ...[ Text( - leg.leg.eyebrow, + [ + if (leg.leg.eyebrow.isNotEmpty) + leg.leg.eyebrow, + if (stopLabel.isNotEmpty) + stopLabel.toUpperCase(), + ].join(' · '), style: TextStyle( fontSize: 10.sp, fontWeight: FontWeight.w800, @@ -279,9 +363,7 @@ class PickupCard extends StatelessWidget { SizedBox(height: 3.h), ], Text( - (dropLeads ? dropName : pickupName).isEmpty - ? 'Not named' - : (dropLeads ? dropName : pickupName), + headlineName.isEmpty ? 'Not named' : headlineName, maxLines: 2, overflow: TextOverflow.ellipsis, style: TextStyle( @@ -312,6 +394,17 @@ class PickupCard extends StatelessWidget { // lost — see [areaOf]. Text( () { + // A base is read at a gate, so it gets its street + // and pincode rather than the locality summary the + // other legs use — "Peelamedu" is not enough to + // find a loading bay by. + if (baseLeads) { + // Empty rather than repeating the name: a base + // with no address on the row has nothing more + // to say here, and printing "Salem Base" twice + // reads as a rendering fault. + return headlineAddress; + } final full = dropLeads ? (dropAddress.isEmpty ? address @@ -1560,7 +1653,7 @@ String _destinationName(Map item) { String _destinationAddress(Map item) { final area = areaOf( item, - preferDrop: ServiceProfile.active.deliversToCustomer, + preferDrop: WorkPolicy.deliversToCustomer(item), ); if (area.isNotEmpty) return area; @@ -1569,6 +1662,6 @@ String _destinationAddress(Map item) { final drop = (item['dropaddress'] ?? item['DropAddress'] ?? '') .toString() .trim(); - if (ServiceProfile.active.deliversToCustomer && drop.isNotEmpty) return drop; + if (WorkPolicy.deliversToCustomer(item) && drop.isNotEmpty) return drop; return (item['pickupaddress'] ?? '').toString().trim(); } diff --git a/lib/views/Dashboard/pickups/delivery_actions.dart b/lib/views/Dashboard/pickups/delivery_actions.dart index b59aed1..1cbbc2f 100644 --- a/lib/views/Dashboard/pickups/delivery_actions.dart +++ b/lib/views/Dashboard/pickups/delivery_actions.dart @@ -36,6 +36,15 @@ enum DeliveryOutcome { /// It is not going to happen. `POST /miler/bookings/:id/cancel`. cancelled, + + /// Handed in at a base, ending this rider's custody. + /// `POST /miler/consignments/:id/inward-at-hub` — see [handOverAtHub]. + /// + /// Not a kind of [delivered]. A base handover has no receiver, no proof + /// photo, no OTP and no COD, and it must never reach `deliver` — that route + /// moves the consignment to `Out_for_Delivery` against a receiver in another + /// district, which is a state the handset cannot undo. + handedOver, } extension DeliveryOutcomeX on DeliveryOutcome { @@ -43,6 +52,7 @@ extension DeliveryOutcomeX on DeliveryOutcome { DeliveryOutcome.delivered => 'Delivered', DeliveryOutcome.skipped => 'Skip', DeliveryOutcome.cancelled => 'Cancelled', + DeliveryOutcome.handedOver => 'Handed over', }; IconData get icon => switch (this) { @@ -54,12 +64,15 @@ extension DeliveryOutcomeX on DeliveryOutcome { // skip-reason sheet's "Delivery paused" row already wears. DeliveryOutcome.skipped => LucideIcons.clock, DeliveryOutcome.cancelled => LucideIcons.circleX, + // A building, not a tick: the parcel is not finished, it has changed hands. + DeliveryOutcome.handedOver => LucideIcons.building2, }; Color get colour => switch (this) { DeliveryOutcome.delivered => ColorConstants.acceptGreen, DeliveryOutcome.skipped => ColorConstants.warning, DeliveryOutcome.cancelled => ColorConstants.errorRed, + DeliveryOutcome.handedOver => ColorConstants.acceptGreen, }; /// What Activity will show once the round is over. @@ -67,6 +80,7 @@ extension DeliveryOutcomeX on DeliveryOutcome { DeliveryOutcome.delivered => 'Delivered', DeliveryOutcome.skipped => 'Skipped', DeliveryOutcome.cancelled => 'Cancelled', + DeliveryOutcome.handedOver => 'Handed over', }; } @@ -583,6 +597,14 @@ Future?> _closeDelivery( // reportable from `Collected_By_Miler` as well as `Out_for_Delivery` — a // customer who is not home is not home whether or not the rider remembered // to press Start round. See [ConsignmentStateX.canSkip]. + // ── A handover is deliberately NOT gated here ── + // + // [DeliverGate] answers the question "may this be *delivered*", and a + // base-routed parcel is `Created` — which it reads as `awaitingInward` and + // refuses with "this parcel hasn't been released for delivery yet". Correct + // for a delivery, exactly wrong for the rung that performs the inward. + // `inward-at-hub` is the server's own judge of its preconditions and answers + // `INVALID_STATE` when they are not met. final gated = outcome == DeliveryOutcome.delivered || outcome == DeliveryOutcome.skipped; @@ -820,6 +842,15 @@ Future?> _closeDelivery( notes: notes, ); + case DeliveryOutcome.handedOver: + // The fence, the consignment lookup and the `inward-at-hub` write all + // live in [handOverAtHub] — this is the one close path, so the record + // that gets filed is the same shape whichever rung produced it. + ok = await handOverAtHub(stop); + if (!ok && lastHandoverFailure != null && context.mounted) { + AppFeedback.error(context, lastHandoverFailure!); + } + case DeliveryOutcome.skipped: // ── Straight to the consignment route ── // @@ -1050,3 +1081,142 @@ Future?> _closeDelivery( 'notes': notes, }; } + +// ═══════════════════════════════════════════════════════════════════════════ +// THE BASE HANDOVER — the leg the rider could not finish +// ═══════════════════════════════════════════════════════════════════════════ + +/// Why the last [handOverAtHub] refused, in the rider's words. Null on success. +String? lastHandoverFailure; + +/// The base this stop must be handed in at, or null when it is not a base leg. +/// +/// Two sources, in order. `next_hub` rides on the row itself (request 25) and +/// is the authoritative one. [PickupLocations.baseFor] is the fallback for a +/// row that names a hub id without expanding it — a shape older payloads use. +HandoverHub? handoverBaseFor(Map stop) { + final fromRow = HandoverHub.from(stop['next_hub'] ?? stop['nexthub']); + if (fromRow != null) return fromRow; + return PickupLocations.baseFor( + stop['next_hub_id'] ?? stop['nexthubid'] ?? stop['hubid'], + ); +} + +/// Hands a base-routed consignment in, ending this rider's custody of it. +/// +/// ── The leg that existed everywhere except the app ── +/// +/// `MilerApi.inwardAtHub` and `MilerLifecycle.inwardAtHub` were both written, +/// documented and covered by `hub_handover_contract_test.dart`. Neither had a +/// single call site. So with `MILER_HUB_HANDOVER_ENABLED` on server-side, a +/// Gandhipuram → Chennai parcel arrived on Deliveries as [NextLeg.hub], +/// [releaseForDelivery] correctly refused to start a customer round for it — +/// telling the rider "hand it over at base" — and there was no control in the +/// app that could record him doing so. The parcel sat in his queue until hub +/// staff inwarded it from the console, and the assignment closed against +/// nobody, so the job reported zero distance and zero value on his earnings. +/// +/// ── Correct in both positions of the server flag ── +/// +/// This never asks which way the flag is set, because the app must not mirror +/// it. With the flag **off**, `pickup-complete` inwards the parcel itself and +/// the row comes back `Inwarded_at_Hub` / `handed_to_hub` — [NextLeg.closed] — +/// so this function is never reached and no control is drawn. With it **on**, +/// the row is `Created` / `inward_at_hub` — [NextLeg.hub] — and this is the +/// rung that finishes it. The leg is read from the row, every time. +/// +/// ── Fenced at the base, like every other presence claim ── +/// +/// 100 m, against the base's own coordinates. A base with no coordinates is +/// refused rather than waved through, for the same reason a customer stop with +/// no pin is: nobody can say afterwards where the rider was standing. +/// +/// Idempotent twice over — the shared `Idempotency-Key` covers a retry after a +/// dropped response, and a parcel already inwarded answers 200 with +/// `already_inwarded: true`, which [MilerLifecycle.inwardAtHub] reads as the +/// success it is. A rider pressing again on bad signal at a loading bay is +/// confirmed, not refused. +Future handOverAtHub(Map stop) async { + lastHandoverFailure = null; + + final orderId = MilkRun.idOf(stop); + final leg = NextLegResolver.resolve( + stop, + pivotAction: (await getPivotNextActions())[orderId] ?? '', + ); + if (!leg.isHub) { + // Not a refusal the rider caused — the row says this parcel is not going to + // a base. Saying so beats posting a handover the server will reject. + lastHandoverFailure = + 'This parcel is not going to a base. Check the stop and try again.'; + debugPrint('[HANDOVER] $orderId is not a base leg — $leg'); + return false; + } + + final base = handoverBaseFor(stop); + + // ── The fence, before anything else is spent ── + // + // A base with no coordinates lands on [GeofenceOutcome.noTarget] and is + // refused with a sentence pointing at the office, which is the only party who + // can add the missing pin. + final decision = await Geofence.check( + targetLat: base?.latitude, + targetLng: base?.longitude, + action: 'Handed over', + ); + if (!decision.allowed) { + lastHandoverFailure = decision.reason; + debugPrint('[HANDOVER] $orderId refused by the fence — $decision'); + return false; + } + + final consignmentId = await resolveConsignmentId(stop); + if (consignmentId.isEmpty) { + lastHandoverFailure = + 'This stop has no shipment reference yet, so it cannot be handed over. ' + 'Ask your office to check it — pressing again will not help.'; + debugPrint('[HANDOVER] no consignment id for $orderId'); + return false; + } + + debugPrint( + '[TRACE][HANDOVER] consignment=$consignmentId base=${base?.id} ' + 'POST /miler/consignments/$consignmentId/inward-at-hub — calling', + ); + final res = await MilerApi.inwardAtHub( + consignmentId, + hubId: (base?.id.isNotEmpty ?? false) ? base!.id : null, + // The position the fence just judged, not a fresh one: two fixes seconds + // apart are two different answers and the hub's history row should carry + // the one the app actually allowed the handover on. + lat: decision.riderLat, + lon: decision.riderLng, + ); + final t = MilerLifecycle.inwardAtHub(res); + MilerLifecycle.report('inward-at-hub', t); + debugPrint( + '[TRACE][HANDOVER] consignment=$consignmentId -> ${res.status} ' + '${res.code} confirmed=${t.isConfirmed} raw=${res.raw}', + ); + + if (t.isConfirmed) return true; + + // ── A 200 that names no state is not proof ── + // + // The rule request 15 exists for, and the one this app has been bitten by on + // `reached`: a bare success is not a transition. The rider is not shown a + // handover the hub may not have recorded. + if (t.isUnconfirmed) { + lastHandoverFailure = + 'Your office did not confirm the handover. Check with the base before ' + 'you leave the parcel.'; + return false; + } + + final serverMsg = (res.message).trim(); + lastHandoverFailure = serverMsg.isNotEmpty && serverMsg.length < 140 + ? 'The base would not accept this parcel: $serverMsg' + : 'The base would not accept this parcel. Ask your office to check it.'; + return false; +} diff --git a/lib/views/Dashboard/pickups/map.dart b/lib/views/Dashboard/pickups/map.dart index af58917..cee2fd3 100644 --- a/lib/views/Dashboard/pickups/map.dart +++ b/lib/views/Dashboard/pickups/map.dart @@ -193,6 +193,11 @@ class _PickupMapScreenState extends State<_PickupMapScreen> @override void initState() { super.initState(); + // Which leg this stop is on. Async because it reads the pivot store, so the + // sheet opens in its delivery shape and corrects itself a frame later — + // the base CTA is behind a slide, so there is no window in which the rider + // can commit the wrong rung. + unawaited(_resolveHubLeg()); _camera = MilerMapCamera(controller: _mapController, vsync: this); _routeAnim = AnimationController( @@ -1360,6 +1365,73 @@ class _PickupMapScreenState extends State<_PickupMapScreen> collectedIds: widget.parentState?._collectedIds ?? const {}, ).isDelivery; + /// True when this stop ends at a base rather than at a receiver's door. + /// + /// Read from the row's own `next_action`, never from the rider's line — the + /// same resolver [handOverAtHub] and the Deliveries queue use, so the card, + /// the sheet and the write cannot disagree about where a parcel is going. + /// + /// Held in state rather than computed in `build` because resolving it reads + /// the pivot store, which is async. + bool _isHubLeg = false; + + /// The base this stop is handed in at, once [_resolveHubLeg] has looked. + HandoverHub? _handoverBase; + + /// Resolves [_isHubLeg] once, on open. + /// + /// Silent on failure: a stop whose leg cannot be read falls back to the + /// delivery shape it had before this existed, which is the behaviour every + /// other screen already has. + Future _resolveHubLeg() async { + try { + final leg = NextLegResolver.resolve( + widget.pickup, + pivotAction: + (await getPivotNextActions())[MilkRun.idOf(widget.pickup)] ?? '', + ); + if (!mounted) return; + final base = leg.isHub ? handoverBaseFor(widget.pickup) : null; + setState(() { + _isHubLeg = leg.isHub; + _handoverBase = base; + // ── The destination is the base, not the door he collected at ── + // + // `MilkRun.navigatesToCustomer` is false for a base leg — correctly, a + // base-routed parcel must never point at its receiver 500 km away — so + // `_pickupLocation` fell back to the **pickup** coordinates and the map + // sent the rider back to the customer he had just collected from. + // + // A base with no coordinates is left alone: navigating to `0, 0` lands + // in the Gulf of Guinea. The handover is still refused by the fence in + // that case, with a sentence pointing at the office. + if (base != null && base.isNavigable) { + _pickupLocation = LatLng(base.latitude, base.longitude); + } + }); + } catch (e) { + debugPrint('[HANDOVER] could not resolve the leg: $e'); + } + } + + /// Hands the parcel in at the base and closes the stop. + /// + /// The base-routed twin of [_markDelivered]: same shape, different rung. It + /// does not open [DeliveryProofPage] — there is no receiver to photograph and + /// no OTP to take; the evidence the contract asks for is the position, which + /// [handOverAtHub] stamps from the fix the fence judged. + Future _handOverAtBase() async { + if (_isNavigating || !mounted) return; + HapticFeedback.mediumImpact(); + // Straight through the one close path. [closeDelivery] routes + // `handedOver` to [handOverAtHub] — which takes the fence, resolves the + // consignment and posts `inward-at-hub` — and then files the same finished + // record every other outcome files, so Activity, the carried set and the + // queue all see one kind of closed stop. Calling [handOverAtHub] here as + // well would post the handover twice. + await _closeDelivery(DeliveryOutcome.handedOver); + } + /// True once the rider has set off from **this screen** — slid Start /// delivery, or opened navigation on the collection leg. /// @@ -1447,6 +1519,30 @@ class _PickupMapScreenState extends State<_PickupMapScreen> // [MilkRun.deliveryArrivalIsLocalOnly]), so a button for it would write // nothing. Once he sets off the stop is simply **active**, and the only // question left is whether it was handed over. + // ── A base handover is its own rung ── + // + // It is not a delivery: there is no receiver, no proof photo, no OTP and no + // "customer was out" outcome. It is also not nothing, which is what the app + // offered before — [releaseForDelivery] refuses a base leg, correctly, and + // the rider was left holding a parcel with a message telling him to hand it + // over and no control that would record it. + if (_isHubLeg) { + final onTheRoad = _setOff; + // Named where it is known. "Handed over at Coimbatore Base" is a rider + // confirming a place; "Handed over at base" is him confirming a category, + // and on a round with two bases that is the difference between a parcel + // arriving and a parcel being looked for. + final base = _handoverBase?.name ?? ''; + return MilerSlideAction( + label: onTheRoad + ? (base.isEmpty ? 'Handed over at base' : 'Handed over at $base') + : 'Slide to start ride', + icon: onTheRoad ? LucideIcons.check : LucideIcons.chevronRight, + color: ColorConstants.acceptGreen, + onCommit: onTheRoad ? _handOverAtBase : _startRideToBase, + ); + } + if (_isDeliveryLeg) { final onTheRoad = _setOff; final slide = MilerSlideAction( @@ -1572,6 +1668,16 @@ class _PickupMapScreenState extends State<_PickupMapScreen> return false; } + // ── Not refused, but not recorded either ── + // + // The rung advances locally below, which is right — a dead network must + // not strand a rider at a door. What was wrong is that it advanced + // *silently*: the rider saw ARRIVED and could not tell the difference + // between a stop the hub knows about and one it does not. + if (!ok && dc.lastArrivalNotice != null && mounted) { + AppFeedback.warn(context, dc.lastArrivalNotice!); + } + // The rung the rider sees, on the row he is looking at and in the store // that survives the refresh this flow triggers. `reached` does not // persist on every deployment yet — see `getArrivedOrderIds` — and the @@ -1607,8 +1713,100 @@ class _PickupMapScreenState extends State<_PickupMapScreen> /// here — no photo, a dead signature, a refused PUT — resolves to an empty /// string, which is what this call site was sending unconditionally until /// now. The local copy stays on the device either way. + /// The parcel photograph's storage references, once uploaded. Null until it + /// has been, and after a failed attempt — a failure is not a photograph. + UploadRef? _parcelProof; + + /// Uploads the door photograph once per stop. + /// + /// Best-effort by design: a dead object store must not strand a rider at a + /// customer's door holding parcels he cannot record. When it fails the parcel + /// update goes without `photos` — which is honest, and better than an empty + /// array claiming no photograph was taken. + Future _ensureParcelProofUploaded(int bookingId) async { + if (_parcelProof != null || bookingId <= 0) return; + final path = (_verificationProofPath ?? '').trim(); + if (path.isEmpty) return; + + final file = File(path); + if (!file.existsSync()) return; + + // Kept on the device first, whatever the network does — see ProofStore. + try { + final orderId = (widget.pickup['orderid'] ?? '').toString(); + if (orderId.isNotEmpty) await ProofStore.save(orderId, path); + } catch (e) { + debugPrint('[PICKUP] could not keep a local copy of the proof: $e'); + } + + try { + _parcelProof = await MilerApi.uploadProofRef( + file, + purpose: MilerApi.proofPickup, + // The BOOKING id, in the booking field. There is no consignment yet — + // `pickup-complete` has not run — and this used to pass the booking id + // in the `consignmentid` slot, filing every pickup proof under a + // consignment number that did not exist. + bookingId: bookingId, + ); + } catch (e) { + debugPrint('[PICKUP] parcel proof upload failed: $e'); + } + + if (_parcelProof == null || !_parcelProof!.hasKey) { + ApiConfig.logGap( + 'uploads/sign', + 'the parcel photo for booking $bookingId could not be uploaded (or the ' + 'server returned no key); the parcels are being recorded without ' + 'one and the copy stays on the device.', + ); + } + } + + /// The local path of the photograph the verification screen took, if any. + String? _verificationProofPath; + + /// Copies the uploaded key onto the verification map the provider reads. + Map _withParcelPhotos(Map verification) { + final key = _parcelProof?.key ?? ''; + if (key.isEmpty) return verification; + final pickup = verification['pickup']; + if (pickup is! Map) return verification; + return { + ...verification, + 'pickup': {...pickup, 'photos': [key]}, + }; + } + Future _uploadParcelProof(String path, int bookingId) async { if (path.isEmpty || bookingId <= 0) return ''; + + // Already sent, on the way to `POST /parcel`. Pushing the same bytes twice + // costs a rider at a doorstep several seconds on a mobile connection and + // leaves two copies of one photograph in the bucket. + await _ensureParcelProofUploaded(bookingId); + if (_parcelProof != null) return _parcelProof!.url; + + // ── Kept on the device first, whatever the network does ── + // + // The uploaded URL goes nowhere: `pickup-complete` takes latitude and + // longitude and nothing else, so even a successful upload leaves the hub + // with no reference to the photograph (see BE-2, and the gap logged in + // `UpdatePickupProvider`). The rider was being made to photograph the + // parcels — the flow will not let him confirm without it — for a record + // that existed only in a bucket nobody could search. + // + // A local copy against the order id is the one form of this evidence that + // is actually retrievable today: the office rings the rider and he has it. + // Best-effort, and deliberately before the upload, because the case where + // it matters most is the one where the network is the problem. + try { + final orderId = (widget.pickup['orderid'] ?? '').toString(); + if (orderId.isNotEmpty) await ProofStore.save(orderId, path); + } catch (e) { + debugPrint('[PICKUP] could not keep a local copy of the proof: $e'); + } + try { final url = await MilerApi.uploadProof( File(path), @@ -1686,6 +1884,22 @@ class _PickupMapScreenState extends State<_PickupMapScreen> await _openGoogleMapsNavigation(); } + /// Sets off for the base. + /// + /// Deliberately **not** [_startDelivery]. `start-delivery` moves the + /// consignment to `Out_for_Delivery` against a receiver in another district; + /// the hub then sees a rider actively delivering a parcel that is going onto + /// a line-haul truck, and there is no way back from that state on the + /// handset. A base leg has no release rung at all — the rider simply rides + /// there, and the next thing the server hears is the handover. + Future _startRideToBase() async { + if (_isNavigating || !mounted) return; + setState(() => _setOff = true); + HapticFeedback.mediumImpact(); + _hasOpenedNavigation = false; + await _openGoogleMapsNavigation(); + } + /// Records a stop that could not be handed over. /// /// The same chooser the proof page shows, reached without having to slide @@ -1845,7 +2059,15 @@ class _PickupMapScreenState extends State<_PickupMapScreen> Map verifyResult = {'verified': true}; - if (ServiceProfile.active.needsVerification) { + // ── Asked of the stop, not of the rider ── + // + // `ServiceProfile.active.needsVerification` gives the same answer for + // every row on the screen. On a meal tenant that walked a CX customer + // pickup straight past the door flow — no parcel count, no destination, + // no weight — and the booking pivoted on whatever the customer typed into + // the app days earlier. [WorkPolicy] asks the row, and falls back to the + // line only where the row says nothing. + if (WorkPolicy.capturesShipmentDetails(widget.pickup)) { final verified = await openScreen>( context, StopVerificationPage(pickup: widget.pickup), @@ -1870,7 +2092,7 @@ class _PickupMapScreenState extends State<_PickupMapScreen> // customer. Its answers ride along in the same map, and // `_startPickupNavigation` sends them — addresses first, because // `pickup-complete` routes the consignment from them. - if (ServiceProfile.active.capturesShipmentAddresses) { + if (WorkPolicy.capturesShipmentAddresses(widget.pickup)) { final seeded = Map.from(widget.pickup); // Don't ask for the weight twice: the verification page has just taken // it, so the desk opens with it filled in. @@ -2038,6 +2260,12 @@ class _PickupMapScreenState extends State<_PickupMapScreen> if (_isNavigating || !mounted) return; setState(() => _isNavigating = true); + // The photograph the verification screen took, remembered so the parcel + // update and the pickup-complete proof both use the one upload. + _verificationProofPath = (verificationData['parcelImage'] ?? '') + .toString() + .trim(); + try { final dc = Get.put(PickupsController(), permanent: true); final d = widget.pickup; @@ -2184,9 +2412,23 @@ class _PickupMapScreenState extends State<_PickupMapScreen> } if (pickupId > 0) { + // ── The photograph is uploaded HERE, before the parcels are sent ── + // + // It used to be uploaded much later, inside `_payAndCreateOrder`, and + // only its URL was kept — so `POST /parcel` went out with no `photos` + // and the evidence ended up in a bucket nobody could search. `photos` + // wants the storage **key**, so the upload has to happen before this + // call, not after it. + // + // Uploaded once and remembered: `_payAndCreateOrder` reuses the same + // ref for `proofImage` rather than pushing the same bytes twice. + await _ensureParcelProofUploaded(pickupId); + final parcelRes = await UpdatePickupProvider().submitParcels( pickupId, - verificationData, + // The key rides in on the verification map the provider already + // reads, so no third source of truth about this door. + _withParcelPhotos(verificationData), ); if (parcelRes?['status'] != true) { debugPrint('[PARCEL] booking $pickupId: not recorded — $parcelRes'); @@ -2220,7 +2462,7 @@ class _PickupMapScreenState extends State<_PickupMapScreen> // the compliance stamp, the completed record, the next-stop list and the // hand-off screen — runs once and unchanged for either. final dynamic result = - (ServiceProfile.active.initiatesShipment && shipmentCapture != null) + (WorkPolicy.initiatesShipment(widget.pickup) && shipmentCapture != null) ? await openScreen>( context, ShipmentReviewPage( diff --git a/lib/views/Dashboard/pickups/pickups.dart b/lib/views/Dashboard/pickups/pickups.dart index ddb8190..8ea28b8 100644 --- a/lib/views/Dashboard/pickups/pickups.dart +++ b/lib/views/Dashboard/pickups/pickups.dart @@ -15,7 +15,7 @@ library; import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:flutter/foundation.dart' show kDebugMode, mapEquals, setEquals; +import 'package:flutter/foundation.dart' show mapEquals, setEquals; import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:miler/views/helpers/constants/design_constants.dart'; @@ -82,6 +82,10 @@ import 'package:miler/data/service_day.dart'; import 'package:miler/data/work_domain.dart'; import 'package:miler/data/work_repository.dart'; import 'package:miler/data/miler_api.dart'; +import 'package:miler/data/lifecycle.dart'; +import 'package:miler/data/geofence.dart'; +import 'package:miler/data/task_profile.dart'; +import 'package:miler/data/pickup_locations.dart'; import 'package:miler/data/assignment_lookup.dart'; import 'package:miler/utils/external_navigation.dart'; @@ -943,33 +947,10 @@ class _MyPickupsState extends State fontFamily: FontConstants.fontFamily, ), ), - _fetchDiagLine(), ], ); } - /// The last fetch's stage counts, shown under an empty state in debug only. - /// Never compiled into release — [kDebugMode] is a const, so the whole widget - /// folds away. - Widget _fetchDiagLine() { - if (!kDebugMode || _fetchDiag.isEmpty) return const SizedBox.shrink(); - return Padding( - padding: EdgeInsets.only(top: 18.h), - child: Text( - 'debug · $_fetchDiag\n' - '${_bookingTrips.length} trip(s) · showing ${_visibleStops.length}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 10.sp, - height: 1.5, - fontWeight: FontWeight.w600, - color: ColorConstants.secondaryText, - fontFamily: FontConstants.fontFamily, - ), - ), - ); - } - Widget _buildTripRail() { // The whole run, finished stops included — see [_railStops]. Drawn as long // as *anything* happened today, so a rider who has just closed his last @@ -2182,8 +2163,10 @@ class _MyPickupsState extends State // separate causes have made this tab look empty — a gate on the raw API // count, a missing empty state on an unfilled trip tab, and stops // filtered out as still-pending — and every one of them looked identical - // on screen. The counts are now shown on the empty state itself, so the - // failing stage is visible instead of guessed at. + // on screen. The counts go to the log so the failing stage can be read + // off a `flutter logs` rather than guessed at. They are deliberately NOT + // drawn on the empty state any more: that put developer diagnostics in + // front of the rider on every debug and demo build. _fetchDiag = // Which line the app resolved the rider onto. Three of the reports // that landed here as "the tab is wrong" were really "the app thinks @@ -3118,9 +3101,16 @@ class _MyPickupsState extends State // is the picture now, at a size worth looking at, and // it keeps its own copy. // - // [_fetchDiagLine] stays: it is `kDebugMode`-gated and - // folds away entirely in release, so nothing ships - // underneath the art. + // Nothing is drawn under the art. A stage-count + // readout used to sit here — `debug · line=milkMan + // api=0 accepted=0 …` — on the argument that it was + // `kDebugMode`-gated and folded away in release. + // True, and it still meant every internal build, + // every demo and every screenshot showed a rider a + // line of diagnostics under a picture telling him he + // was all caught up. The counts are still gathered + // and still logged — see `[MYPICKUPS][DIAG]` — where + // the person who needs them is looking. child: Center( child: Column( mainAxisSize: MainAxisSize.min, @@ -3140,7 +3130,6 @@ class _MyPickupsState extends State ), ), ), - _fetchDiagLine(), ], ), ), diff --git a/lib/views/Dashboard/pickups/sheet.dart b/lib/views/Dashboard/pickups/sheet.dart index aae32b3..b171e58 100644 --- a/lib/views/Dashboard/pickups/sheet.dart +++ b/lib/views/Dashboard/pickups/sheet.dart @@ -193,7 +193,7 @@ class _PickupBottomSheetState extends State<_PickupBottomSheet> // stays "Confirm pickup". final String confirmLabel = isDelivery ? 'Confirm delivery' - : (ServiceProfile.active.initiatesShipment + : (WorkPolicy.initiatesShipment(widget.pickup) ? 'Initiate order' : 'Confirm pickup'); diff --git a/lib/views/Dashboard/profile/Profilepage.dart b/lib/views/Dashboard/profile/Profilepage.dart index 001f126..06194c1 100644 --- a/lib/views/Dashboard/profile/Profilepage.dart +++ b/lib/views/Dashboard/profile/Profilepage.dart @@ -35,6 +35,7 @@ import 'package:miler/controllers/profile_controller.dart'; import 'package:miler/data/service_profile.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:miler/data/accepted_store.dart'; +import 'package:miler/data/session.dart'; import 'package:miler/data/proof_store.dart'; import 'package:miler/controllers/rewards_controller.dart'; import 'package:miler/controllers/summary_controller.dart'; @@ -866,8 +867,32 @@ void _showLogoutDialog(BuildContext context) { // Doorstep photos are exactly the kind of record that must // not outlive the session that took them. await ProofStore.clearScope(); - final prefs = await SharedPreferences.getInstance(); - await prefs.setBool('logged_out', true); + + // ── The session ends here, not at the next sign-in ── + // + // This did not clear the bearer token. The rider was sent + // to the sign-in screen and `logged_out` kept him there, so + // it *looked* finished — but the token stayed in + // SharedPreferences under `authtoken`, still valid, until + // the next `verifyPinWithServer` happened to overwrite it. + // + // Two things followed. Anything that reads the token + // without checking the flag — a background isolate, the + // notification handler, a heartbeat that outlives the + // route change — could go on making authenticated calls as + // the rider who just left. And a handset handed to the next + // rider carried the previous one's credential on disk. + // + // Logging out is the one moment the app is certain the + // session is over. The credential goes then. + // Everything above plus the token, the in-flight guard, the + // cached fix and the `logged_out` flag now live in one + // place — see [endSession]. The other caller is the 401 + // handler in `main.dart`: a session the server has stopped + // accepting has to end exactly as thoroughly as one the + // rider chose to end, and when the two were written out + // separately only this one existed. + await endSession(); Get.offAll(() => const SignIn()); }, height: ButtonSizes.secondary, diff --git a/lib/views/helpers/widgets/app_widgets.dart b/lib/views/helpers/widgets/app_widgets.dart index 8d20ede..57fb9b5 100644 --- a/lib/views/helpers/widgets/app_widgets.dart +++ b/lib/views/helpers/widgets/app_widgets.dart @@ -1856,6 +1856,23 @@ class AppFeedback { duration: const Duration(seconds: 2), ); + /// It went through on the phone but not to the office. + /// + /// The band between [error] and [success], and the app needed one: a status + /// the rider set with no signal is neither. Refusing it would strand him at a + /// door; a green tick would tell him the hub knows, which it does not. Amber + /// says "done here, not there", which is the only true thing available. + /// + /// Longer than [info] on purpose — it carries a consequence the rider may + /// need to act on later. + static void warn(BuildContext context, String message) => _show( + context, + message, + icon: LucideIcons.triangleAlert, + fill: ColorConstants.warning, + duration: const Duration(seconds: 5), + ); + /// Neutral news: something changed that the rider did not ask for, or a /// background action finished. Never used for failures. static void info(BuildContext context, String message) => _show( diff --git a/lib/views/offline/offline_banner.dart b/lib/views/offline/offline_banner.dart index 5d2d4ad..72bfe4f 100644 --- a/lib/views/offline/offline_banner.dart +++ b/lib/views/offline/offline_banner.dart @@ -25,10 +25,23 @@ import 'package:miler/views/helpers/constants/miler_type.dart'; /// ── Why a strip is the right shape ── /// /// Everything the rider needs at the door was already fetched. Connectivity is -/// needed to *report* the stop, not to work it. So the honest message is not -/// "you are offline" but "keep going, this will sync" — and that is a line of +/// needed to *report* the stop, not to work it. So the right shape is a line of /// text, not a screen. /// +/// ── What it must not say ── +/// +/// It said "Changes will sync when you're back online". **There is no mutation +/// queue in this app** — no outbox, no replay, no retry loop. A status the +/// rider sets with no signal is attempted once and fails, and nothing ever +/// sends it again. The banner was promising a mechanism that does not exist, +/// to the one person who would be blamed when the hub had no record of the +/// stop. +/// +/// The copy now says what is actually true: he can keep working the stop, and +/// the *status* needs signal. Building the queue is a real piece of work and a +/// separate one (see the release report); until it exists this line must not +/// imply it. +/// /// The full-screen page survives for the one case where it is truthful: a cold /// start with no cached session, where there genuinely is nothing to show. /// @@ -46,7 +59,7 @@ class OfflineBanner extends StatelessWidget { Widget build(BuildContext context) { return Semantics( liveRegion: true, - label: 'Offline. Your changes will sync when you are back online.', + label: 'Offline. Status updates need a connection. Stop details are still available.', excludeSemantics: true, child: Material( color: ColorConstants.warning, @@ -64,9 +77,10 @@ class OfflineBanner extends StatelessWidget { SizedBox(width: 9.w), Expanded( child: Text( - // States what is true and what happens next. "You're - // offline" alone tells a rider mid-stop nothing he can use. - 'Offline · Changes will sync when you’re back online', + // States what is true and what he can still do. It must + // not promise a sync — nothing in this app replays a failed + // mutation. See the class note. + 'Offline · Status updates need a connection', maxLines: 2, overflow: TextOverflow.ellipsis, style: MilerType.micro.on(ColorConstants.onAccent).semibold, diff --git a/lib/views/onboardscreens/Creat_mpin.dart b/lib/views/onboardscreens/Creat_mpin.dart index a8aa15a..aa96c99 100644 --- a/lib/views/onboardscreens/Creat_mpin.dart +++ b/lib/views/onboardscreens/Creat_mpin.dart @@ -6,6 +6,8 @@ import 'package:miler/views/helpers/widgets/page_transitions.dart'; import 'package:miler/controllers/auth.dart'; import 'package:miler/views/helpers/constants/Font_constant.dart'; import 'package:miler/views/helpers/constants/Colorconstants.dart'; +import 'package:miler/widget/Bottom_page.dart'; +import 'package:miler/views/helpers/widgets/app_widgets.dart'; import 'package:miler/views/onboardscreens/Mpin.dart'; import 'package:miler/views/onboardscreens/auth_scaffold.dart'; @@ -178,11 +180,35 @@ class _CreateMpinBodyState extends State<_CreateMpinBody> { final ok = await _auth.setPin(_pin(_newMpinControllers)); if (!mounted) return; setState(() => isLoading = false); + + // ── Setting the PIN IS the sign-in ── + // + // `POST /miler/set-pin` returns a full session — token and user — so there + // is no verify-pin afterwards and no second PIN to type. This used to push + // the rider to the unlock screen to enter the PIN he had just chosen, which + // was the only thing it could do while the write had no route behind it. if (ok) { - openScreen(context, Mpin()); - } else { - setState(() => _error = 'Could not save your PIN. Please try again.'); + Get.offAll(() => const BottomPage()); + return; } + + // A 409 means he is not a first-time rider after all — he already has a + // PIN, here or on another handset. Sending him to Enter-PIN is the useful + // answer; "could not save your PIN" would leave him retyping one the + // server will never accept. + if (_auth.lastSetPinWasAlreadySet) { + if (_auth.lastSetPinFailure != null) { + AppFeedback.infoGlobal(_auth.lastSetPinFailure!); + } + openScreen(context, Mpin()); + return; + } + + setState( + () => _error = + _auth.lastSetPinFailure ?? + 'Could not save your PIN. Please try again.', + ); } @override diff --git a/lib/views/onboardscreens/Sign_in.dart b/lib/views/onboardscreens/Sign_in.dart index ca347d1..0f85f99 100644 --- a/lib/views/onboardscreens/Sign_in.dart +++ b/lib/views/onboardscreens/Sign_in.dart @@ -4,7 +4,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:miler/views/helpers/widgets/page_transitions.dart'; -import 'package:miler/views/onboardscreens/otp_page.dart'; +import 'package:miler/views/onboardscreens/Creat_mpin.dart'; import 'package:miler/views/onboardscreens/Mpin.dart'; import 'package:miler/views/onboardscreens/auth_scaffold.dart'; import 'package:miler/controllers/auth.dart'; @@ -138,21 +138,43 @@ class _SignInState extends State { if (!mounted) return; setState(() => isLoading = false); - if (decision == AuthNext.otp) { - await controller.auth.sendOtp(phoneController.text); - if (!mounted) return; - openScreen(context, OtpPage()); - } else if (decision == AuthNext.verifyPin) { - openScreen(context, Mpin()); - } else { - // notRegistered / error. Reported on the screen rather than in a snackbar - // under the keyboard, where the old version put it. - setState(() { - _showError = true; - _errorText = - 'We could not sign you in with that number. Check it and try ' - 'again, or contact your manager.'; - }); + // ── The server decides which screen, on one boolean ── + // + // `POST /miler/login` answers `pin_set`. False means a rider who has never + // signed in — the console no longer issues PINs, so he sets his own. True + // is the path every existing rider has always taken. + // + // This used to branch to an OTP screen when the directory said "no such + // account": the code was never verified (`verifyOtp` returned true without + // checking) and it dead-ended at a Create-MPIN screen with no route to + // call. A rider who took it could not get back. + switch (decision) { + case AuthNext.setPin: + openScreen(context, CreateMpin()); + case AuthNext.verifyPin: + openScreen(context, Mpin()); + case AuthNext.notRegistered: + setState(() { + _showError = true; + _errorText = + 'That number is not registered as a Miler. Check it, or ask ' + 'your manager to add you.'; + }); + case AuthNext.inactive: + setState(() { + _showError = true; + _errorText = + 'This account is not active. Contact your manager.'; + }); + case AuthNext.error: + // The directory could not be reached. Not a fact about the rider, and + // deliberately worded as the temporary thing it is. + setState(() { + _showError = true; + _errorText = + 'We could not reach the server. Check your connection and try ' + 'again.'; + }); } } diff --git a/test/activity_trips_test.dart b/test/activity_trips_test.dart index 8388e71..284503d 100644 --- a/test/activity_trips_test.dart +++ b/test/activity_trips_test.dart @@ -207,10 +207,22 @@ void main() { ); }); - test('the other line’s records are not this one’s', () async { + test('but the other line’s records ARE this one’s', () async { + // Reversed 2026-09-16 with the removal of the line from [WorkScope]. + // Under one-Miler this rule lost a rider his own work: a meal drop and a + // parcel collection finished in the same shift are both his, and filing + // them in two drawers meant Activity showed him half his day and no + // explanation for the other half. + // + // Ownership is still enforced — see the test above, which is the one that + // was actually protecting anything. await addCompletedBookings([done('a')], terminalStatus: 'delivered'); ServiceProfile.setActive(ServiceProfile.parcel); - expect(await getCompletedOrderIds(), isEmpty); + expect( + await getCompletedOrderIds(), + contains('a'), + reason: 'one rider, one day, one history', + ); }); }); diff --git a/test/arrived_needs_the_rider_there_test.dart b/test/arrived_needs_the_rider_there_test.dart new file mode 100644 index 0000000..4730a4d --- /dev/null +++ b/test/arrived_needs_the_rider_there_test.dart @@ -0,0 +1,103 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// ARRIVED IS A REPORT OF WHERE THE RIDER IS, SO IT HAS TO BE TRUE +/// +/// `_advanceStop` writes the ARRIVED rung optimistically, and the reasoning for +/// that is sound and stays: a rider standing at a counter with no signal must +/// not have his own report snapped back because a poll was late. +/// +/// What was wrong is that `updateArrivedStatus` answers `false` for THREE +/// different things and the caller treated all three as that one: +/// +/// 1. the request never landed — no signal. Optimistic is right. +/// 2. **the geofence refused** — the rider is 1.4km from the door and no +/// request was ever made. +/// 3. **the server refused** — it was asked and said no, on a business rule. +/// +/// On 2 and 3 the app wrote ARRIVED onto a stop the rider had not reached. The +/// admin console only ever hears what the server was told, so it went on showing +/// the stop as pending — arrived on the phone, not arrived in the office, with +/// no way for either side to know which was right. That is the divergence +/// reported from the field. +/// +/// Read from source: `_advanceStop` is a private method on a State class in a +/// 4,000-line page, and standing up GetX, ScreenUtil, Geolocator and a live +/// controller to assert one branch would test the harness more than the rule. +/// The regression class here is *a missing guard*, and the cheapest honest +/// check for a missing guard is to look for it. +/// ───────────────────────────────────────────────────────────────────────── +void main() { + final homepage = File( + 'lib/views/Dashboard/home/homepage.dart', + ).readAsStringSync(); + + final controller = File( + 'lib/controllers/pickups_controller.dart', + ).readAsStringSync(); + + group('a blocked arrival does not advance the rung', () { + test('the caller consults the geofence verdict before advancing', () { + expect( + homepage, + contains('dc.lastBlockedReason'), + reason: + 'Without this the optimistic branch advances ARRIVED on a stop the ' + 'geofence just refused, and the console never hears about it.', + ); + }); + + test('it also stops on a refusal the server actually sent', () { + expect( + homepage, + contains('dc.lastArrivalRefusal'), + reason: + 'A 4xx is the office saying no. Advancing past it shows the rider ' + 'as arrived somewhere the office has on record he is not.', + ); + }); + + test('the reason is handed back, not swallowed', () { + // A rider who is simply told "refused" slides again at a stop that will + // keep refusing. The geofence reason carries the distance and the target. + expect(homepage, contains('if (notThere != null) return notThere;')); + }); + }); + + group('a dead network still advances', () { + test('the optimistic path is intact', () { + // The fix narrowed the optimistic branch; it did not delete it. A rider + // at a counter with no signal still gets his rung, and + // `lastArrivalNotice` is what tells him the office has not been told. + expect(controller, contains('lastArrivalNotice')); + expect( + controller, + contains('Marked arrived on your phone'), + reason: 'the no-signal notice is the half that must NOT become a block', + ); + }); + + test('a transport failure sets no refusal, so it falls through', () { + // `lastArrivalRefusal` is assigned null when the code is below 400 — + // that null is load-bearing now, because it is what lets the optimistic + // branch still run for a rider with no signal. + expect(controller, contains('lastArrivalRefusal = code >= 400')); + }); + }); + + group('the verdicts are fresh, not left over', () { + test('an arrival clears both signals on entry', () { + // Read only when the write fails, and the caller now acts on them — so a + // value left from an earlier stop would refuse an arrival because a + // different door refused twenty minutes ago. + expect(controller, contains('lastArrivalRefusal = null')); + expect(controller, contains('lastArrivalNotice = null')); + }); + + test('the geofence check clears its own reason on entry', () { + expect(controller, contains('lastBlockedReason = null')); + }); + }); +} diff --git a/test/base_handover_card_test.dart b/test/base_handover_card_test.dart new file mode 100644 index 0000000..8d5f4f5 --- /dev/null +++ b/test/base_handover_card_test.dart @@ -0,0 +1,233 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:miler/data/api_config.dart'; +import 'package:miler/data/service_profile.dart'; +import 'package:miler/views/Dashboard/pickups/pickups.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// A HANDOVER CARD NAMES THE BASE IT IS SENDING HIM TO +/// +/// The card already refused to lead with the receiver, and was right to — on a +/// base leg he is in another district, and heading the card with him is the +/// navigation mistake that rule exists to prevent. +/// +/// But refusing the receiver only left the *pickup*, so a handover card read: +/// +/// BASE HANDOVER +/// Anitha R +/// Gandhipuram +/// +/// …the customer he had collected from an hour earlier, under a heading telling +/// him to go to a base. The place he was actually going appeared nowhere on the +/// card — only on the sheet's slider, after he had already set off. On a round +/// with two bases that is a parcel left in the wrong building. +/// +/// `next_hub` rides on the row (the backend sends all six fields: id, name, +/// address, pincode, latitude, longitude), so the destination is knowable at +/// card-build time and there is no excuse for the card not to say it. +/// ───────────────────────────────────────────────────────────────────────── +void main() { + setUp(() => ServiceProfile.setActive(ServiceProfile.parcel)); + tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel)); + + Future pump(WidgetTester tester, Widget child) async { + tester.view.physicalSize = const Size(1170, 2532); + tester.view.devicePixelRatio = 3.0; + addTearDown(tester.view.resetPhysicalSize); + addTearDown(tester.view.resetDevicePixelRatio); + + await tester.pumpWidget( + ScreenUtilInit( + designSize: const Size(390, 844), + builder: (_, _) => MaterialApp( + home: Scaffold(body: SingleChildScrollView(child: child)), + ), + ), + ); + await tester.pumpAndSettle(); + } + + /// A collected parcel routed to a base — the row as the queue returns it with + /// `MILER_HUB_HANDOVER_ENABLED` on. + Map hubStop({Map? hub}) => + ApiConfig.pickupFromBooking({ + 'bookingid': 4821, + 'orderid': 'ORDER-HUB-1', + 'status': 'Converted_To_Consignment', + 'consignmentstatus': 'Created', + 'consignmentid': 77, + 'next_action': 'inward_at_hub', + 'sourcename': 'Anitha R', + 'pickupcustomer': 'Anitha R', + 'pickupaddress': '12 Gandhipuram, Coimbatore', + 'recipientname': 'Ravi Kumar', + 'dropaddress': '88 Anna Nagar, Chennai 600040', + 'next_hub': + hub ?? + const { + 'id': 1, + 'name': 'Coimbatore Base', + 'address': '14 Avinashi Road, Peelamedu', + 'pincode': '641004', + 'latitude': 11.0272, + 'longitude': 76.9905, + }, + }); + + /// The same parcel, going to a customer instead. + Map customerStop() => ApiConfig.pickupFromBooking(const { + 'bookingid': 4822, + 'orderid': 'ORDER-CX-1', + 'consignmentstatus': 'Out_for_Delivery', + 'consignmentid': 78, + 'next_action': 'deliver', + 'pickupcustomer': 'Anitha R', + 'pickupaddress': '12 Gandhipuram, Coimbatore', + 'recipientname': 'Ravi Kumar', + 'dropaddress': '4 Race Course, Coimbatore 641018', + }); + + testWidgets('the BASE leads, not the sender he collected from', ( + tester, + ) async { + await pump( + tester, + PickupCard( + item: hubStop(), + displayStep: 1, + distanceMeters: 4200, + collectedIds: const {'ORDER-HUB-1'}, + ), + ); + + expect(find.text('BASE HANDOVER'), findsOneWidget); + expect( + find.text('Coimbatore Base'), + findsOneWidget, + reason: 'the rider must know WHICH base without opening a sheet', + ); + expect( + find.text('Anitha R'), + findsNothing, + reason: + 'the sender he collected from an hour ago is not the destination', + ); + }); + + testWidgets('the base address is readable at a gate', (tester) async { + await pump( + tester, + PickupCard( + item: hubStop(), + displayStep: 1, + distanceMeters: 4200, + collectedIds: const {'ORDER-HUB-1'}, + ), + ); + + // Street and pincode, not the locality summary the other legs use — + // "Peelamedu" alone does not find a loading bay. + expect( + find.textContaining('14 Avinashi Road'), + findsOneWidget, + ); + expect(find.textContaining('641004'), findsOneWidget); + }); + + testWidgets('the distant receiver never takes the headline', (tester) async { + await pump( + tester, + PickupCard( + item: hubStop(), + displayStep: 1, + distanceMeters: 4200, + collectedIds: const {'ORDER-HUB-1'}, + ), + ); + + // The rule that predates this change and must survive it: a Chennai + // receiver on a parcel going to a Coimbatore base must not be drawn as the + // place the rider is heading. + expect(find.text('Ravi Kumar'), findsNothing); + }); + + testWidgets('a base with no coordinates still names itself', (tester) async { + // It is a caption rather than a destination — navigating to 0,0 lands in + // the Gulf of Guinea — but the rider still needs to read which base it is. + // The geofence refuses the handover separately, with a sentence pointing at + // the office. + await pump( + tester, + PickupCard( + item: hubStop(hub: const {'id': 2, 'name': 'Salem Base'}), + displayStep: 1, + distanceMeters: 4200, + collectedIds: const {'ORDER-HUB-1'}, + ), + ); + + expect(find.text('BASE HANDOVER'), findsOneWidget); + // Once — the name is the headline, and the address line stays empty rather + // than repeating it. + expect(find.text('Salem Base'), findsOneWidget); + }); + + testWidgets('a customer delivery still leads with the RECIPIENT', ( + tester, + ) async { + // The other half of the contract. This change must not push the base + // treatment onto a leg that has a real receiver — that fix (recipient, not + // sender) was itself a production bug once. + await pump( + tester, + PickupCard( + item: customerStop(), + displayStep: 1, + distanceMeters: 1400, + collectedIds: const {'ORDER-CX-1'}, + ), + ); + + expect(find.text('CUSTOMER DELIVERY'), findsOneWidget); + expect(find.text('Ravi Kumar'), findsOneWidget); + + // The sender IS still on the card, and should be: once the parcel is in the + // box the pickup is history, but it is what establishes custody and on a + // run with two counters it is how he knows which shelf the label came off. + // It sits under a hairline at half the weight — so "leads" is a claim about + // TYPE SIZE, and that is what gets asserted rather than mere presence. + expect(find.text('Anitha R'), findsOneWidget); + + double sizeOf(String text) => + tester.widget(find.text(text)).style?.fontSize ?? 0; + + expect( + sizeOf('Ravi Kumar'), + greaterThan(sizeOf('Anitha R')), + reason: 'the receiver leads; the sender is context beneath him', + ); + }); + + testWidgets('on a base leg the sender is not drawn as context either', ( + tester, + ) async { + // The context block is gated on `dropLeads`, which is false for a base leg + // — so a handover card carries the base and nothing that could be mistaken + // for a destination. + await pump( + tester, + PickupCard( + item: hubStop(), + displayStep: 1, + distanceMeters: 4200, + collectedIds: const {'ORDER-HUB-1'}, + ), + ); + + expect(find.text('Anitha R'), findsNothing); + expect(find.text('Ravi Kumar'), findsNothing); + expect(find.text('Coimbatore Base'), findsOneWidget); + }); +} diff --git a/test/deliver_identity_test.dart b/test/deliver_identity_test.dart index 759c54f..3c3a8db 100644 --- a/test/deliver_identity_test.dart +++ b/test/deliver_identity_test.dart @@ -56,22 +56,35 @@ void main() { ); }); - test('the mapping is per rider and per line', () async { + test('the mapping is per rider — and follows him across lines', () async { + // ── This asserted the opposite until 2026-09-16 ── + // + // The scope key carried a third part, the rider's service line: + // `consignment_ids::u38.t13.milkMan`. That was right while a rider was + // one thing for the life of his account. He is not — one Miler carries + // meal parcels, logistics collections and customer pickups in the same + // shift — and keying his records by a line split one day's work across + // two drawers, with whichever drawer the app resolved into being the only + // one he could see. + // + // For a *consignment id* that was not a cosmetic loss: the id is what + // `deliver` and `skip` key on, so a stop collected while the app read one + // line and delivered after it read the other could not be closed at all. + // The rider was told "this order was never picked up on the system". await rememberConsignmentId('ORDER-1', '46'); ServiceProfile.setActive(ServiceProfile.parcel); expect( (await getConsignmentIds())['ORDER-1'], - isNull, - reason: "one line's consignment mapping is not another's", + '46', + reason: 'one rider, one day, one drawer — whatever he is carrying', ); SharedPreferences.setMockInitialValues({'userid': 99}); - ServiceProfile.setActive(ServiceProfile.milkMan); expect( (await getConsignmentIds())['ORDER-1'], isNull, - reason: "another rider's handset drawer is not this one", + reason: "another rider's handset drawer is still not this one", ); }); diff --git a/test/delivery_leg_test.dart b/test/delivery_leg_test.dart index 3afc1c7..e7fc60f 100644 --- a/test/delivery_leg_test.dart +++ b/test/delivery_leg_test.dart @@ -27,22 +27,39 @@ void main() { tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel)); - group('the three outcomes', () { - test('are exactly delivered, skip and cancelled', () { + group('the four ways a stop in custody can end', () { + test('are delivered, skip, cancelled and handed over', () { + // ── `handedOver` was added 2026-09-16 ── + // + // There used to be three, and the missing fourth is why a base-routed + // parcel could not be closed at all: `MilerApi.inwardAtHub` existed, was + // documented and was covered by `hub_handover_contract_test.dart`, and + // had no call site. A rider carrying a Gandhipuram → Chennai consignment + // was told "hand it over at base" by `releaseForDelivery` and given no + // control that would record him doing so. + // + // It is deliberately NOT a kind of `delivered`: a base handover has no + // receiver, no proof photo, no OTP and no COD, and it must never reach + // `deliver` — that route moves the consignment to `Out_for_Delivery` + // against a receiver in another district, which the handset cannot undo. expect(DeliveryOutcome.values.map((o) => o.label), [ 'Delivered', 'Skip', 'Cancelled', + 'Handed over', ]); }); test('delivered is the only one that completes the order', () { // Skip is a return visit — it increments `attemptcount` rather than - // failing the parcel — and cancelled is a withdrawal. Neither is a - // delivery, and Activity must not file them as one. + // failing the parcel — and cancelled is a withdrawal. Handed over ends + // this RIDER's custody while the parcel's own journey carries on inside + // the network. None of the three is a delivery, and Activity must not + // file them as one. expect(DeliveryOutcome.delivered.pastTense, 'Delivered'); expect(DeliveryOutcome.skipped.pastTense, 'Skipped'); expect(DeliveryOutcome.cancelled.pastTense, 'Cancelled'); + expect(DeliveryOutcome.handedOver.pastTense, 'Handed over'); }); test('each one is coloured by what it means, not by where it sits', () { diff --git a/test/delivery_proof_test.dart b/test/delivery_proof_test.dart index 065ae42..e7b11a3 100644 --- a/test/delivery_proof_test.dart +++ b/test/delivery_proof_test.dart @@ -141,10 +141,16 @@ void main() { ); }); - test('the other line cannot either', () async { + test('but the same rider can, whatever line he is reading', () async { + // Reversed 2026-09-16 with the removal of the line from [WorkScope]. + // A doorstep photo is evidence the rider produced; hiding it from him + // because a tenant label resolved differently after a refresh is how a + // proof goes missing at exactly the moment somebody disputes a delivery. + // + // The ownership rule above is untouched and is the one that matters. await ProofStore.save('ORDER-1', (await cameraShot('a')).path); ServiceProfile.setActive(ServiceProfile.parcel); - expect(await ProofStore.pathFor('ORDER-1'), isNull); + expect(await ProofStore.pathFor('ORDER-1'), isNotNull); }); test('logout deletes the photos, not just the index', () async { diff --git a/test/geofence_flag_test.dart b/test/geofence_flag_test.dart index f07c8e1..aabfa58 100644 --- a/test/geofence_flag_test.dart +++ b/test/geofence_flag_test.dart @@ -1,67 +1,92 @@ import 'package:flutter_test/flutter_test.dart'; -import 'package:miler/controllers/pickups_controller.dart'; +import 'package:miler/data/geofence.dart'; -/// Whichever way the proximity fence is set, it must be set **once** and on -/// purpose. +/// ───────────────────────────────────────────────────────────────────────── +/// THE SWITCH, AND THE FOUR SHAPES THAT MUST NOT COME BACK /// -/// [kGeofenceEnforced] decides whether "Picked up" means the rider was standing -/// at the door or merely pressed a button, and it has already been wrong in -/// three directions: it was `kDebugMode` — so every debug build had no fence -/// and the fence was therefore never tested — then a hard `false`, so the flow -/// could not be walked at a desk at all, then a define defaulting to off. +/// This file used to assert the opposite of what it asserts now, and that is +/// the point of keeping it rather than deleting it. /// -/// **It is OFF, decided 2026-08-25.** It was turned on that morning with a -/// radius of [kGeofenceRadiusMeters] and turned off again the same day, at the -/// founder's call. Nothing was found wrong with it — this is a decision about -/// *when* to switch it on. +/// It read: /// -/// So the direction this file guards has flipped twice in a day, which is -/// exactly the pattern that made the old defaults dangerous, and it is why the -/// three tests below are not all about the direction. The measurement work is -/// unchanged and still asserted: a `best` fix with a shelf life, the phone's -/// error credited to the rider, and one radius rather than three. All of it is -/// dormant while [kGeofenceEnforced] is false and none of it needs revisiting -/// on the day it goes back on. +/// expect(kGeofenceEnforced, isFalse); // "recorded decision", 2026-08-25 +/// expect(kGeofenceRadiusMeters, 10); +/// +/// So the test suite was **enforcing the release blocker**: a developer who +/// turned the fence on to fix it would be met with a red test telling him the +/// off state was deliberate. Between them those two lines meant every shipped +/// build let a rider mark a stop picked up from anywhere on earth, and the +/// suite agreed that was correct. +/// +/// Reversed 2026-09-16 as part of the Play Store release audit. The fence now +/// lives in `lib/data/geofence.dart` and the behaviour is exercised properly in +/// `geofence_test.dart` — 41 cases, the full distance table, every failure +/// mode, and an HTTP client that fails the test if a blocked rung posts +/// anything. +/// +/// What is left here is the short list of *specific past mistakes*, each of +/// which shipped, so that none of them can return quietly. +/// ───────────────────────────────────────────────────────────────────────── void main() { - test('there is one switch, and the legacy name derives from it', () { - // Two constants that could disagree is exactly how a build ends up with - // the fence on in one gate and off in the other — the bulk check on Home - // and the per-stop check in the controller are separate code paths that - // must never answer differently. - expect(kBypassGeofenceForTesting, !kGeofenceEnforced); - }); - - test('enforcement is off, and that is the recorded decision', () { - // If this fails, one of two things happened, and both should be read rather - // than silenced: - // - // • the default was turned back on — record the date and the reason here, - // as the previous flips did; or - // • this run passed `--dart-define=ENFORCE_GEOFENCE=true`, in which case - // the failure is correct and is telling you this build has the fence on. + test('MISTAKE 1 — the fence must not default to off', () { + // `bool.fromEnvironment('ENFORCE_GEOFENCE', defaultValue: false)`. Every + // `flutter build appbundle --release` passes no dart-define, so the default + // *is* the shipped behaviour. It had been `kDebugMode` before that — off in + // exactly the builds anyone tested with — and a hard `false` before that. + // Three spellings, one effect. expect( kGeofenceEnforced, - isFalse, + isTrue, reason: - 'Proximity enforcement is expected to be OFF (see kGeofenceEnforced). ' - 'Either the default was restored, or this run passed ' - '--dart-define=ENFORCE_GEOFENCE=true.', + 'The release build must enforce proximity. If this run passed ' + '--dart-define=ENFORCE_GEOFENCE=false, this failure is correct.', ); }); - test('the radius is one number, and it is the tight one', () { - // Asserted while the fence is off, deliberately. The app used to hold - // three — a configured `pickupradius` defaulting to 100 in the controller, - // a hardcoded 500 in Home's bulk gate, and no agreement between them — so - // which fence a rider met depended on whether he ticked boxes or slid a - // sheet. Whichever way the switch above is set, that must not come back. - expect(kGeofenceRadiusMeters, 10); + test('MISTAKE 2 — the radius must not be smaller than the GPS', () { + // 10 m is at or inside the error radius of consumer GPS, so the fence + // stopped measuring proximity and started measuring whether the satellites + // were kind. It refused riders standing at doors, which is what got the + // whole control switched off twice. + expect(kGeofenceRadiusMetres, 100); + expect( + kGeofenceRadiusMetres, + greaterThan(kGeofenceMaxAccuracyMetres), + reason: 'a fence smaller than the fix that measures it cannot be met', + ); }); - test('a cached fix has a shelf life', () { - // On a round, a stale position is reliably the *previous* stop. Anything - // much longer than this and the fence starts measuring from the last door. + test('MISTAKE 3 — there must be exactly one radius', () { + // The app once held three at the same time: a per-tenant `pickupradius` + // from prefs defaulting to 100, a hardcoded 500 in Home's bulk gate, and 10 + // in the controller. Which fence a rider met depended on whether he ticked + // boxes on Home or slid the sheet on a stop. + // + // Both gates call `Geofence.check` now and neither holds arithmetic, so + // this asserts the constant is the only knob there is to turn. + expect(kGeofenceRadiusMetres, isA()); + expect(kGeofenceRadiusMetres, greaterThan(0)); + }); + + test('MISTAKE 4 — a cached fix must have a shelf life', () { + // A last-known position is instant, free, and can be an hour old. On a + // round it is reliably the *previous* stop, which is how a rider marks a + // delivery arrived from the last street he was on. expect(kGeofenceFixMaxAge, const Duration(seconds: 30)); }); + + test('the fence still has a way to be switched off in an emergency', () { + // Deliberately kept, and deliberately not the default. If the fence starts + // refusing real work at real doors, `--dart-define=ENFORCE_GEOFENCE=false` + // gets the fleet moving inside one release rather than one sprint — and + // every bypassed check logs in every build mode, so a build's own log says + // which way it was compiled. + // + // Raise `kGeofenceRadiusMetres` before reaching for it. + expect( + const bool.fromEnvironment('ENFORCE_GEOFENCE', defaultValue: true), + kGeofenceEnforced, + ); + }); } diff --git a/test/geofence_test.dart b/test/geofence_test.dart new file mode 100644 index 0000000..bc7ff00 --- /dev/null +++ b/test/geofence_test.dart @@ -0,0 +1,588 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:http/http.dart' as http; +import 'package:http/testing.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:miler/controllers/pickups_controller.dart'; +import 'package:miler/data/geofence.dart'; +import 'package:miler/data/miler_api.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// THE PROXIMITY FENCE +/// +/// This is the control that decides whether "Picked up" means the rider was +/// standing at the door or merely pressed a button, and it has been wrong in +/// four directions inside one year: compiled off by default, set to a 10 m +/// radius no consumer GPS can resolve, failing **open** on any thrown +/// exception, and crediting the phone's own error to the rider so that a vague +/// fix bought slack rather than a refusal. +/// +/// So the tests below are not a spot check of the arithmetic. They pin, in +/// order: +/// +/// 1. the **switch** — on by default, because that default has flipped twice; +/// 2. the **radius** — 100 m, inclusive at the boundary; +/// 3. the **distance table** the spec asks for, 0 m to 250 m; +/// 4. every way of **failing to measure**, each of which must refuse; +/// 5. the one that matters most — **a blocked rung sends no HTTP request.** +/// +/// (5) is the whole point. A fence that shows a message and posts the status +/// anyway is worse than no fence: the hub is told the rider was there *and* +/// the app claims to have checked. +/// ───────────────────────────────────────────────────────────────────────── + +/// A stop in Coimbatore. Any point would do; a real one keeps the numbers +/// recognisable when a failure prints them. +const double kTargetLat = 11.0272; +const double kTargetLng = 76.9905; + +/// Metres per degree of latitude. Constant enough for a fixture — the tests +/// assert against what [Geolocator.distanceBetween] actually measures, not +/// against this, so the approximation cannot make a test pass wrongly. +const double _metresPerDegreeLat = 111320.0; + +/// A rider fix [metres] due north of the stop. +Position riderAt( + double metres, { + double accuracy = 8, + Duration age = Duration.zero, +}) => Position( + latitude: kTargetLat + (metres / _metresPerDegreeLat), + longitude: kTargetLng, + timestamp: DateTime.now().subtract(age), + accuracy: accuracy, + altitude: 0, + altitudeAccuracy: 0, + heading: 0, + headingAccuracy: 0, + speed: 0, + speedAccuracy: 0, +); + +void useFix(Position p) => + Geofence.positionProvider = () async => GeofenceFix(position: p); + +void useFailure(GeofenceOutcome outcome) => + Geofence.positionProvider = () async => GeofenceFix(failure: outcome); + +void main() { + tearDown(Geofence.useDevice); + + // ─────────────────────────────────────────────────────────────────────── + group('the switch and the radius', () { + test('enforcement is ON by default', () { + // It was `defaultValue: false`, so every release build shipped with the + // fence returning true on its third line. If this fails, either the + // default was reverted — which is the release blocker this file exists to + // prevent — or the run passed `--dart-define=ENFORCE_GEOFENCE=false`, in + // which case the failure is correct and is telling you so. + expect( + kGeofenceEnforced, + isTrue, + reason: + 'Proximity enforcement must be ON by default. Either the default ' + 'was reverted, or this run passed ENFORCE_GEOFENCE=false.', + ); + }); + + test('the radius is 100 metres, and there is only one of them', () { + // The app once held three: a configured `pickupradius` defaulting to 100, + // a hardcoded 500 in Home's bulk gate, and 10 in the controller. Which + // fence a rider met depended on whether he ticked boxes or slid a sheet. + expect(kGeofenceRadiusMetres, 100); + }); + + test('one fix may serve a batch, but only briefly', () { + // [kGeofenceFixReuse] is what makes a twenty-order bulk action cost one + // GPS settle instead of twenty. It is a performance change to a safety + // control, so the two bounds that keep it honest are asserted here. + expect( + kGeofenceFixReuse, + lessThanOrEqualTo(kGeofenceFixMaxAge), + reason: + 'the reuse window must sit inside the staleness rule — a fix too ' + 'old to measure with cannot become acceptable by being cached', + ); + // At walking pace (~1.4 m/s) the window is ~20 m of possible drift + // against a 100 m fence. Riding away covers the radius inside it, so a + // rider who genuinely leaves is refused on the next real fix rather than + // carried to the next door by the cached one. + expect( + kGeofenceFixReuse.inSeconds * 1.4, + lessThan(kGeofenceRadiusMetres / 2), + reason: 'drift during the window must stay well under the radius', + ); + }); + + test('resetting the cache is available to sign-out and tests', () { + // A cached position is a fact about a rider. It must not outlive him on a + // shared handset. + Geofence.resetCache(); + Geofence.useDevice(); + }); + + test('a cached fix has a shelf life, and a vague one is rejected', () { + expect(kGeofenceFixMaxAge, const Duration(seconds: 30)); + expect( + kGeofenceMaxAccuracyMetres, + lessThan(kGeofenceRadiusMetres), + reason: + 'a fix whose error is as large as the fence cannot resolve it, and ' + 'must be refused rather than credited', + ); + }); + }); + + // ─────────────────────────────────────────────────────────────────────── + group('the distance table', () { + // Every case the spec names. Each asserts the *rule* against what + // Geolocator actually measured, so a fixture that drifts by a metre cannot + // quietly invert a boundary case. + for (final metres in const [0.0, 25.0, 50.0, 99.0, 100.0, 101.0, 125.0, + 250.0]) { + test('${metres.round()} m', () async { + final pos = riderAt(metres); + useFix(pos); + + final measured = Geofence.distanceBetween( + kTargetLat, + kTargetLng, + pos.latitude, + pos.longitude, + ); + expect( + measured, + closeTo(metres, 1.0), + reason: 'fixture should sit where it claims', + ); + + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + + final shouldAllow = measured <= kGeofenceRadiusMetres; + expect( + d.allowed, + shouldAllow, + reason: + 'at ${measured.toStringAsFixed(1)} m against a ' + '${kGeofenceRadiusMetres.round()} m fence', + ); + expect( + d.outcome, + shouldAllow ? GeofenceOutcome.inside : GeofenceOutcome.outside, + ); + expect(d.distanceMetres, closeTo(measured, 0.001)); + expect(d.accuracyMetres, 8); + expect(d.timestamp, isNotNull); + }); + } + + test('the boundary is inclusive — 100 m is in, a hair over is out', () { + // Stated as arithmetic rather than as a fixture, because this is the one + // place a `<` for a `<=` changes a rider's day and no GPS fixture can be + // placed accurately enough to catch it. + expect(99.999 <= kGeofenceRadiusMetres, isTrue); + expect(100.0 <= kGeofenceRadiusMetres, isTrue); + expect(100.001 <= kGeofenceRadiusMetres, isFalse); + }); + + test('a refusal tells the rider how far, and in a unit he uses', () async { + useFix(riderAt(240)); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.reason, contains('240 m')); + expect(d.reason, contains('100 m')); + expect(d.reason, contains('arrived')); + // Never a developer error, a status code or a coordinate pair. + expect(d.reason, isNot(contains('Exception'))); + expect(d.reason, isNot(contains('null'))); + }); + + test('over a kilometre reads in kilometres', () async { + useFix(riderAt(4200)); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Delivered', + ); + expect(d.reason, contains('4.2 km')); + expect(d.reason, contains('delivered')); + }); + + test('the verb follows the rung', () async { + useFix(riderAt(500)); + for (final action in const [ + 'Arrived', + 'Picked', + 'Picked up', + 'Delivery arrived', + 'Delivered', + 'Handed over', + ]) { + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: action, + ); + expect(d.allowed, isFalse, reason: action); + expect(d.reason, contains(action.toLowerCase()), reason: action); + } + }); + }); + + // ─────────────────────────────────────────────────────────────────────── + group('every way of failing to measure is a refusal', () { + // The old implementation allowed on four of these six. That is what made + // the fence decorative: switching location off opened every rung. + + test('GPS switched off', () async { + useFailure(GeofenceOutcome.serviceDisabled); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.serviceDisabled); + expect(d.reason, contains('Location is switched off')); + }); + + test('permission denied', () async { + useFailure(GeofenceOutcome.permissionDenied); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Delivered', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.permissionDenied); + expect(d.reason, contains('Allow location access')); + }); + + test('permission denied forever points at Settings', () async { + useFailure(GeofenceOutcome.permissionDeniedForever); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.permissionDeniedForever); + expect(d.reason, contains('Settings')); + }); + + test('a timeout waiting for the fix', () async { + useFailure(GeofenceOutcome.timeout); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.timeout); + }); + + test('a stale fix is not a measurement', () async { + // On a round, a stale position is reliably the *previous* stop — which is + // how a rider marks a delivery arrived from the last street. + useFailure(GeofenceOutcome.staleFix); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.staleFix); + }); + + test('a fix too vague to resolve the fence is refused, not credited', + () async { + // The inversion this replaces: the old check was + // `distance - accuracy > radius`, so a ±250 m fix bought 250 m of slack. + // The worse the fix, the easier it was to pass. Here the rider is 10 m + // away — comfortably inside — and is still refused, because the phone + // cannot show that he is. + useFix(riderAt(10, accuracy: kGeofenceMaxAccuracyMetres + 1)); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.poorAccuracy); + expect(d.reason, contains('Step into the open')); + expect(d.accuracyMetres, kGeofenceMaxAccuracyMetres + 1); + }); + + test('a vague fix cannot pass even when it is standing on the stop', + () async { + useFix(riderAt(0, accuracy: 500)); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Delivered', + ); + expect(d.allowed, isFalse, reason: 'distance 0 must not beat accuracy'); + }); + + test('a fix at the accuracy ceiling is still usable', () async { + useFix(riderAt(20, accuracy: kGeofenceMaxAccuracyMetres)); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isTrue, reason: 'the ceiling is inclusive'); + }); + + test('the provider throwing is a refusal, not a pass', () async { + // This is the `catch { return true; // fail-safe }` that used to open + // every rung in the app on any throw from the location stack. + Geofence.positionProvider = () async => throw Exception('platform'); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.error); + expect(d.reason, isNotNull); + expect(d.reason, isNot(contains('platform')), + reason: 'the rider must not be shown the exception'); + }); + + test('a rider fix of 0,0 is absent, not the Gulf of Guinea', () async { + useFix( + Position( + latitude: 0, + longitude: 0, + timestamp: DateTime.now(), + accuracy: 5, + altitude: 0, + altitudeAccuracy: 0, + heading: 0, + headingAccuracy: 0, + speed: 0, + speedAccuracy: 0, + ), + ); + final d = await Geofence.check( + targetLat: kTargetLat, + targetLng: kTargetLng, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.noFix); + }); + }); + + // ─────────────────────────────────────────────────────────────────────── + group('a stop with no coordinates', () { + // This used to return true — "Missing coordinates. Proceeding with update." + // A booking with a blank latitude therefore opened every rung on that stop, + // which is the bypass that makes a fence decorative. + for (final pair in const [ + (null, null, 'both null'), + (0.0, 0.0, 'both zero'), + (kTargetLat, 0.0, 'longitude zero'), + (0.0, kTargetLng, 'latitude zero'), + (95.0, kTargetLng, 'latitude out of range'), + (kTargetLat, 200.0, 'longitude out of range'), + ]) { + test('is refused — ${pair.$3}', () async { + useFix(riderAt(0)); + final d = await Geofence.check( + targetLat: pair.$1, + targetLng: pair.$2, + action: 'Arrived', + ); + expect(d.allowed, isFalse); + expect(d.outcome, GeofenceOutcome.noTarget); + // The rider cannot fix the hub's data from a doorstep, so the sentence + // sends him to the people who can. + expect(d.reason, contains('office')); + }); + } + + test('it does not even ask the phone for a fix', () async { + // An 8-second GPS settle to answer a question with no target is time + // taken off a rider's round for nothing. + var asked = false; + Geofence.positionProvider = () async { + asked = true; + return GeofenceFix(position: riderAt(0)); + }; + await Geofence.check( + targetLat: null, + targetLng: null, + action: 'Arrived', + ); + expect(asked, isFalse); + }); + }); + + // ─────────────────────────────────────────────────────────────────────── + group('BLOCKED MEANS NO REQUEST IS SENT', () { + // The rule the whole control rests on. Every one of these drives the real + // `PickupsController` rung with an HTTP client that fails the test if it is + // ever called — so this asserts the *call sites*, not the policy object. + + late PickupsController controller; + late List sent; + + setUp(() { + SharedPreferences.setMockInitialValues({'userid': 38}); + sent = []; + MilerApi.client = MockClient((req) async { + sent.add('${req.method} ${req.url.path}'); + // A realistic `reached` reply, in the shape production actually + // returns — `success` is the envelope key (`ApiConfig.toLegacyEnvelope` + // reads it; `status` is ignored), and the arrival stamp sits inside + // `data`. A bare 200 is deliberately NOT read as a transition (see + // `MilerLifecycle.reached`), so a thin fixture would fail the positive + // case below for a reason that has nothing to do with the fence. + return http.Response( + '{"success":true,"data":{"bookingid":4821,' + '"status":"Miler_Assigned","reachedat":"2026-09-16T10:14:02Z"}}', + 200, + ); + }); + controller = PickupsController(); + }); + + tearDown(() { + MilerApi.client = http.Client(); + Geofence.useDevice(); + }); + + test('Arrived, 250 m away — no request', () async { + useFix(riderAt(250)); + final ok = await controller.updateArrivedStatus( + pickupId: 4821, + orderHeaderId: 4821, + pickupLat: '$kTargetLat', + pickupLng: '$kTargetLng', + ); + expect(ok, isFalse); + expect(sent, isEmpty, reason: 'the fence refused; nothing may be posted'); + expect(controller.lastBlockedReason, contains('Move within 100 m')); + }); + + test('Arrived with GPS off — no request', () async { + useFailure(GeofenceOutcome.serviceDisabled); + final ok = await controller.updateArrivedStatus( + pickupId: 4821, + orderHeaderId: 4821, + pickupLat: '$kTargetLat', + pickupLng: '$kTargetLng', + ); + expect(ok, isFalse); + expect(sent, isEmpty); + }); + + test('Arrived with permission denied forever — no request', () async { + useFailure(GeofenceOutcome.permissionDeniedForever); + final ok = await controller.updateArrivedStatus( + pickupId: 4821, + orderHeaderId: 4821, + pickupLat: '$kTargetLat', + pickupLng: '$kTargetLng', + ); + expect(ok, isFalse); + expect(sent, isEmpty); + }); + + test('Arrived on a stop with no pin — no request', () async { + useFix(riderAt(0)); + final ok = await controller.updateArrivedStatus( + pickupId: 4821, + orderHeaderId: 4821, + pickupLat: '0', + pickupLng: '0', + ); + expect(ok, isFalse); + expect( + sent, + isEmpty, + reason: + 'a booking with no coordinates used to be waved through — that is ' + 'the bypass that made the fence decorative', + ); + }); + + test('Delivery arrived, 250 m away — no request', () async { + useFix(riderAt(250)); + final ok = await controller.updateDeliveryArrived( + pickupId: 4821, + dropLat: '$kTargetLat', + dropLng: '$kTargetLng', + ); + expect(ok, isFalse); + expect(sent, isEmpty); + }); + + test('Delivery arrived with NO drop pin — no request', () async { + // The conditional that used to wrap this call site — + // `if (dLat != 0 && dLng != 0)` — skipped the fence silently for exactly + // the stops where nobody could say afterwards where the rider was. + useFix(riderAt(0)); + final ok = await controller.updateDeliveryArrived( + pickupId: 4821, + dropLat: '0', + dropLng: '0', + ); + expect(ok, isFalse); + expect(sent, isEmpty); + }); + + test('Delivered, 250 m away — no request', () async { + useFix(riderAt(250)); + final ok = await controller.updateDeliveredStatus( + pickupId: 4821, + consignmentId: '77', + deliveredToName: 'Anitha R', + dropLat: '$kTargetLat', + dropLng: '$kTargetLng', + ); + expect(ok, isFalse); + expect(sent, isEmpty); + }); + + test('Delivered with NO drop pin — no request', () async { + useFix(riderAt(0)); + final ok = await controller.updateDeliveredStatus( + pickupId: 4821, + consignmentId: '77', + deliveredToName: 'Anitha R', + dropLat: '0', + dropLng: '0', + ); + expect(ok, isFalse); + expect(sent, isEmpty); + }); + + test('inside the fence, the request DOES go out', () async { + // The other half of the contract, and the reason this group is not just + // an assertion that everything is broken. + useFix(riderAt(40)); + final ok = await controller.updateArrivedStatus( + pickupId: 4821, + orderHeaderId: 4821, + pickupLat: '$kTargetLat', + pickupLng: '$kTargetLng', + ); + expect(ok, isTrue); + expect(sent, isNotEmpty); + expect(sent.single, contains('/miler/bookings/4821/reached')); + expect(controller.lastBlockedReason, isNull); + }); + }); +} diff --git a/test/hub_handover_action_test.dart b/test/hub_handover_action_test.dart new file mode 100644 index 0000000..f26fedc --- /dev/null +++ b/test/hub_handover_action_test.dart @@ -0,0 +1,249 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:http/testing.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:miler/data/api_config.dart'; +import 'package:miler/data/geofence.dart'; +import 'package:miler/data/miler_api.dart'; +import 'package:miler/data/next_leg.dart'; +import 'package:miler/data/service_profile.dart'; +import 'package:miler/views/Dashboard/pickups/pickups.dart'; + +import 'geofence_test.dart' show kTargetLat, kTargetLng, riderAt; + +/// ───────────────────────────────────────────────────────────────────────── +/// THE BASE HANDOVER, AS A RIDER CAN NOW ACTUALLY PERFORM IT +/// +/// `MilerApi.inwardAtHub` and `MilerLifecycle.inwardAtHub` were written, +/// documented and covered by `hub_handover_contract_test.dart` — and had **zero +/// call sites**. So with `MILER_HUB_HANDOVER_ENABLED` on server-side, a +/// Gandhipuram → Chennai parcel arrived on Deliveries as [NextLeg.hub], +/// `releaseForDelivery` correctly refused to start a customer round for it, and +/// nothing in the app could record the rider handing it in. It sat in his queue +/// until hub staff inwarded it from a console, and the assignment closed +/// against nobody — so the job reported zero distance and zero value on his +/// earnings. +/// +/// `hub_handover_contract_test.dart` still owns the *contract* — the adapter +/// fields, the `next_action` vocabulary, both positions of the server flag, and +/// how the response is read. This file owns the *action*: that a rider standing +/// at the base can close the leg, that one standing anywhere else cannot, and +/// that nothing is posted when the fence refuses. +/// ───────────────────────────────────────────────────────────────────────── +void main() { + late List sent; + late List bodies; + + /// The row as `GET /miler/bookings` returns it with the server flag ON: the + /// pivot left the consignment `Created` and pointed the rider at a base. + Map hubRow() => ApiConfig.pickupFromBooking({ + 'bookingid': 4821, + 'orderid': 'ORDER-HUB-1', + 'status': 'Converted_To_Consignment', + 'consignmentstatus': 'Created', + 'consignmentid': 77, + 'next_action': 'inward_at_hub', + 'next_hub': { + 'id': 1, + 'name': 'Coimbatore Base', + 'address': '14 Avinashi Road, Peelamedu', + 'pincode': '641004', + 'latitude': kTargetLat, + 'longitude': kTargetLng, + }, + }); + + setUp(() { + ServiceProfile.setActive(ServiceProfile.parcel); + SharedPreferences.setMockInitialValues({'userid': 38, 'authtoken': 't'}); + sent = []; + bodies = []; + MilerApi.client = MockClient((req) async { + sent.add('${req.method} ${req.url.path}'); + bodies.add(req.body); + return http.Response( + '{"success":true,"data":{"consignmentstatus":"Inwarded_at_Hub",' + '"inwardedat":"2026-09-16T14:22:10Z","next_action":"handed_to_hub",' + '"already_inwarded":false}}', + 200, + ); + }); + }); + + tearDown(() { + MilerApi.client = http.Client(); + Geofence.useDevice(); + ServiceProfile.setActive(ServiceProfile.parcel); + }); + + group('the leg is read from the row', () { + test('a base-routed row resolves to a base leg and names its base', () { + final row = hubRow(); + expect(NextLegResolver.resolve(row).leg, NextLeg.hub); + + final base = handoverBaseFor(row); + expect(base, isNotNull); + expect(base!.name, 'Coimbatore Base'); + expect(base.isNavigable, isTrue); + }); + + test('a customer-routed row is not a base leg and has no base', () { + final row = ApiConfig.pickupFromBooking({ + 'bookingid': 4822, + 'orderid': 'ORDER-CX-1', + 'consignmentstatus': 'Out_for_Delivery', + 'next_action': 'deliver', + }); + expect(NextLegResolver.resolve(row).leg, NextLeg.customer); + expect(handoverBaseFor(row), isNull); + }); + }); + + group('the fence guards the handover like every other presence claim', () { + test('250 m from the base — refused, and NOTHING is posted', () async { + Geofence.positionProvider = + () async => GeofenceFix(position: riderAt(250)); + + final ok = await handOverAtHub(hubRow()); + + expect(ok, isFalse); + expect(sent, isEmpty, reason: 'a refused handover must not reach inward-at-hub'); + expect(lastHandoverFailure, contains('Move within 100 m')); + expect(lastHandoverFailure, contains('handed over')); + }); + + test('GPS off — refused, and nothing is posted', () async { + Geofence.positionProvider = + () async => const GeofenceFix(failure: GeofenceOutcome.serviceDisabled); + + expect(await handOverAtHub(hubRow()), isFalse); + expect(sent, isEmpty); + expect(lastHandoverFailure, contains('Location is switched off')); + }); + + test('a base with no coordinates is refused, not waved through', () async { + // The old doctrine allowed a target-less stop through on the argument + // that the rider cannot fix the hub's data from a doorstep. True — and it + // made the fence optional for exactly the stops nobody could audit. + Geofence.positionProvider = () async => GeofenceFix(position: riderAt(0)); + + final row = hubRow(); + row['next_hub'] = const {'id': 2, 'name': 'Salem Base'}; + + expect(await handOverAtHub(row), isFalse); + expect(sent, isEmpty); + expect(lastHandoverFailure, contains('office')); + }); + }); + + group('inside the fence, the leg actually closes', () { + setUp(() { + Geofence.positionProvider = () async => GeofenceFix(position: riderAt(30)); + }); + + test('it posts inward-at-hub, with the base and the judged position', + () async { + final ok = await handOverAtHub(hubRow()); + + expect(ok, isTrue); + expect(sent, hasLength(1)); + expect(sent.single, contains('/miler/consignments/77/inward-at-hub')); + + // The body the contract asks for: the base it is reconciled against, and + // the coordinates stamped onto the history row as evidence of where the + // hand-over happened. + expect(bodies.single, contains('"hub_id":"1"')); + expect(bodies.single, contains('"latitude"')); + expect(bodies.single, contains('"longitude"')); + expect(lastHandoverFailure, isNull); + }); + + test('the position posted is the one the fence judged', () async { + final fix = riderAt(30); + Geofence.positionProvider = () async => GeofenceFix(position: fix); + + await handOverAtHub(hubRow()); + + // Not a second GPS read. Two fixes seconds apart are two different + // answers, and the hub should be told the one the app allowed on. + expect(bodies.single, contains(fix.latitude.toString())); + }); + + test('a parcel already inwarded is a success, not a failure', () async { + // He handed it over, the reply was lost, he pressed again at a loading + // bay with one bar of signal. The parcel is on the base's counter and he + // cannot prove that from where he stands. + MilerApi.client = MockClient((req) async { + sent.add('${req.method} ${req.url.path}'); + return http.Response( + '{"success":true,"data":{"consignmentstatus":"Inwarded_at_Hub",' + '"already_inwarded":true}}', + 200, + ); + }); + + expect(await handOverAtHub(hubRow()), isTrue); + }); + + test('a 200 that names no state is NOT treated as a handover', () async { + // The rule `reached` taught this app the hard way: a bare success is not + // a transition. A rider must not walk away from a parcel on the strength + // of one. + MilerApi.client = MockClient((req) async { + sent.add('${req.method} ${req.url.path}'); + return http.Response('{"success":true}', 200); + }); + + expect(await handOverAtHub(hubRow()), isFalse); + expect(lastHandoverFailure, contains('did not confirm')); + expect(lastHandoverFailure, contains('before you leave the parcel')); + }); + + test('a server refusal is passed through in the server\'s own words', + () async { + MilerApi.client = MockClient((req) async { + sent.add('${req.method} ${req.url.path}'); + return http.Response( + '{"success":false,"code":"INVALID_STATE",' + '"message":"already out for delivery"}', + 400, + ); + }); + + expect(await handOverAtHub(hubRow()), isFalse); + expect(lastHandoverFailure, contains('already out for delivery')); + }); + }); + + test('a customer leg is never posted to inward-at-hub', () async { + Geofence.positionProvider = () async => GeofenceFix(position: riderAt(0)); + + final row = ApiConfig.pickupFromBooking({ + 'bookingid': 4822, + 'orderid': 'ORDER-CX-1', + 'consignmentstatus': 'Out_for_Delivery', + 'consignmentid': 78, + 'next_action': 'deliver', + }); + + expect(await handOverAtHub(row), isFalse); + expect(sent, isEmpty); + expect(lastHandoverFailure, contains('not going to a base')); + }); + + test('with the server flag OFF the rider never reaches this rung', () { + // `pickup-complete` inwards the parcel itself and the row comes back + // `Inwarded_at_Hub` / `handed_to_hub`, which is [NextLeg.closed]. The sheet + // draws no base CTA and this function is never called. The app reads the + // row and never mirrors the flag — that is what makes one build correct in + // both positions of it. + final row = ApiConfig.pickupFromBooking({ + 'bookingid': 4821, + 'status': 'Converted_To_Consignment', + 'consignmentstatus': 'Inwarded_at_Hub', + 'next_action': 'handed_to_hub', + }); + expect(NextLegResolver.resolve(row).leg, NextLeg.closed); + }); +} diff --git a/test/logout_clears_session_test.dart b/test/logout_clears_session_test.dart new file mode 100644 index 0000000..80d7c5e --- /dev/null +++ b/test/logout_clears_session_test.dart @@ -0,0 +1,185 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:miler/data/api_config.dart'; +import 'package:miler/data/mutation_guard.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// LOGGING OUT ENDS THE SESSION, NOT JUST THE ROUTE +/// +/// The logout handler cleared the scoped stores and the doorstep photos, wrote +/// `logged_out = true`, and sent the rider to the sign-in screen. It did **not** +/// clear the bearer token. So the app *looked* signed out — the flag kept him +/// on the sign-in screen — while `authtoken` sat in SharedPreferences, still +/// valid, until the next `verifyPinWithServer` happened to overwrite it. +/// +/// Two consequences, and the second is the one that matters on a shared +/// handset: +/// +/// * anything that reads the token without consulting the flag — a background +/// isolate, the notification handler, a heartbeat that outlives the route +/// change — could keep making authenticated calls as the rider who left; +/// * a phone handed to the next rider carried the previous one's credential on +/// disk. +/// +/// Two tests, because the bug had two halves: the credential lifecycle (does +/// clearing actually clear?) and the call site (does logout actually call it?). +/// The first would have passed throughout — `clearToken` was never broken, +/// nobody called it — which is exactly why the second one reads the source. +/// ───────────────────────────────────────────────────────────────────────── +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('the credential lifecycle', () { + test('a cleared token does not come back', () async { + SharedPreferences.setMockInitialValues({'authtoken': 'rider-38-session'}); + expect(await ApiConfig.getToken(), 'rider-38-session'); + + await ApiConfig.clearToken(); + + expect( + await ApiConfig.getToken(), + isNull, + reason: 'the next rider on this handset must start with no session', + ); + }); + + test('an empty token reads as no token, not as a session', () async { + // `getToken` returns null for '' deliberately: a blank string is truthy + // enough to pass a `!= null` check, and that is how a signed-out app ends + // up sending `Authorization: Bearer `. + SharedPreferences.setMockInitialValues({'authtoken': ''}); + expect(await ApiConfig.getToken(), isNull); + }); + + test('clearing an already-clear session is not an error', () async { + // Logout runs it unconditionally, including on a handset that never + // signed in — a crash there would strand the rider on the account screen. + SharedPreferences.setMockInitialValues({}); + await ApiConfig.clearToken(); + expect(await ApiConfig.getToken(), isNull); + }); + + test('the in-flight guard does not survive the session', () async { + // A mutation still held when the rider signs out would have its key + // dropped for the *next* session, silently refusing his first press. + await MutationGuard.run('accept:1042', () async { + expect(MutationGuard.isBusy('accept:1042'), isTrue); + return true; + }); + MutationGuard.reset(); + expect(MutationGuard.isBusy('accept:1042'), isFalse); + }); + }); + + group('the teardown itself', () { + // Read from source. The steps are a sequence of calls with no return value + // to assert on, and the regression class is a *missing call* — the cheapest + // honest guard against a missing call is to look for it. + // + // These used to read Profilepage.dart. The teardown moved into + // `endSession` when the 401 path needed to perform exactly the same steps; + // the assertions followed it rather than being deleted. + final source = File('lib/data/session.dart').readAsStringSync(); + + test('it clears the bearer token', () { + expect( + source, + contains('ApiConfig.clearToken()'), + reason: + 'Ending a session must end the credential, not only the route. ' + 'Without this the token outlives the rider on a shared handset.', + ); + }); + + test('it clears the scoped stores and the doorstep photos', () { + expect(source, contains('clearScopedStores()')); + expect(source, contains('ProofStore.clearScope()')); + }); + + test('it raises the flag the launch path reads', () { + // `app_bootstrap` and `main` both branch on this to decide the start + // screen. Clearing the token without it would send a rider to the + // dashboard with no credential. + expect(source, contains("setBool('logged_out', true)")); + }); + + test('it frees any mutation still holding a key', () { + expect(source, contains('MutationGuard.reset()')); + }); + + test('it drops the cached GPS fix', () { + // [Geofence] reuses one live fix across a batch. A position is a fact + // about a rider; the next person on this handset must not have his first + // proximity check measured from where the last one was standing. + expect(source, contains('Geofence.resetCache()')); + }); + }); + + group('both ways out of a session use it', () { + // ── The half that was missing entirely ── + // + // A session ends two ways: the rider presses Log out, or the server stops + // accepting his token. Only the first was implemented. + // + // `MilerApi` drops the token on any 401 and calls `onUnauthorized` — and + // NOTHING EVER ASSIGNED IT. Three references in the whole codebase, all + // inside miler_api.dart, zero assignments. So the credential was deleted + // and nothing else was: the profile stayed on disk, `logged_out` stayed + // false, and the app kept drawing a signed-in dashboard whose every call + // came back `401 authorization header is required`. + // + // Found on a real handset — rider 23, name and tenant on screen, no + // `authtoken` in SharedPreferences, Home / Deliveries / Activity and the + // heartbeat all failing in a loop. The rider sees no jobs and concludes + // there is no work today; nothing anywhere tells him to sign in again. + + test('the manual Log out delegates to the shared teardown', () { + final source = File( + 'lib/views/Dashboard/profile/Profilepage.dart', + ).readAsStringSync(); + expect( + source, + contains('endSession()'), + reason: 'Log out must use the same teardown the 401 path uses, or the ' + 'two drift and one of them stops clearing something.', + ); + }); + + test('the 401 handler is actually assigned', () { + // The assertion that would have caught the shipped bug. `onUnauthorized` + // being declared and called proves nothing — it was both, and null. + final source = File('lib/main.dart').readAsStringSync(); + expect( + source, + contains('MilerApi.onUnauthorized ='), + reason: + 'MilerApi calls onUnauthorized on every 401. Left unassigned, a ' + 'rider whose token expires is dropped into a session that looks ' + 'signed in and can never make a successful call again.', + ); + }); + + test('the 401 handler ends the session and opens sign-in', () { + final source = File('lib/main.dart').readAsStringSync(); + expect(source, contains('endSession()')); + expect( + source, + contains('SignIn()'), + reason: 'clearing the credential without moving the rider leaves him ' + 'on a dashboard that cannot load anything', + ); + }); + + test('a burst of 401s tears down once, not once per call', () { + // A 401 rarely arrives alone: the home poll, the deliveries queue and the + // heartbeat can each get one within the same second. Without a latch the + // rider is pushed at the sign-in screen three times, and GetX stacks + // three routes he then has to dismiss. + final source = File('lib/main.dart').readAsStringSync(); + expect(source, contains('_signingOut')); + }); + }); +} diff --git a/test/miler_contract_regression_test.dart b/test/miler_contract_regression_test.dart index 0fedcd1..60f23e1 100644 --- a/test/miler_contract_regression_test.dart +++ b/test/miler_contract_regression_test.dart @@ -527,16 +527,22 @@ void main() { }, ); - test('the geofence bypass does not stop the write leaving', () async { - // The suspicion this rules out. `_checkGeofence` returns true early when - // enforcement is off and its success branch contains nothing else — so - // the bypass skips the distance *rejection* and nothing more. + test('the provider itself holds no proximity gate', () async { + // ── What this test used to say ── // - // Asserted through the provider rather than by reading the flag, because - // "the request went out with the fence off" is the claim, and the flag is - // only evidence for it. - expect(kBypassGeofenceForTesting, isTrue); - + // `expect(kBypassGeofenceForTesting, isTrue)` — it asserted the fence was + // OFF, and then that the write still went out. The flag is gone: the + // fence is enforced by default now and lives in `lib/data/geofence.dart`. + // + // The claim underneath it survives and is worth keeping: **the fence is + // in the controller, not in the transport.** `UpdatePickupProvider` maps + // a legacy payload onto a v1 route and posts it, full stop. If a + // proximity check ever appears down here there would be two fences again, + // reachable by different paths, which is the shape that gave the app a + // 500 m radius on Home and a 10 m one on a stop. + // + // The controller-level rule — blocked means no request — is asserted + // end-to-end in `geofence_test.dart`. await UpdatePickupProvider().updatePickup({ 'pickupid': 4211, 'orderstatus': 'arrived', @@ -544,7 +550,7 @@ void main() { 'riderslon': '76.955800', }); - expect(sent, isNotEmpty, reason: 'the bypass swallowed the request'); + expect(sent, isNotEmpty, reason: 'the provider swallowed the request'); expect(only('reached').url.path, contains('/4211/reached')); }); }); diff --git a/test/mixed_work_policy_test.dart b/test/mixed_work_policy_test.dart new file mode 100644 index 0000000..723c3cd --- /dev/null +++ b/test/mixed_work_policy_test.dart @@ -0,0 +1,319 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:miler/data/api_config.dart'; +import 'package:miler/data/service_profile.dart'; +import 'package:miler/data/task_profile.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// ONE MILER, MIXED WORK — asked of the job, not of the rider +/// +/// The scenario this exists for, stated as the operation states it: +/// +/// A rider in RS Puram is assigned **10 kitchen parcels and 5 CX customer +/// pickups**. All fifteen are his, in one shift, on one route. +/// +/// Before this, every operational question was answered by +/// `ServiceProfile.active` — a static resolved once from the rider's tenant, +/// giving the *same answer for all fifteen rows*. So on a meal tenant the five +/// CX pickups were walked straight past the door flow (no parcel count, no +/// destination, no weight, no payment) and pivoted on whatever the customer had +/// typed into the app days earlier. On a parcel tenant the reverse: ten kitchen +/// bags with a known manifest asked the rider to itemise each one. +/// +/// [WorkPolicy] is the bridge. It asks the **row**, and falls back to the line +/// only where the row is silent — because most rows still are (see BE-6), and a +/// screen that took `unknown` at face value would lose the milk round its +/// kitchen headings, which is what happened the two previous times a call site +/// was migrated wholesale. +/// +/// These tests pin both halves: the row wins when it speaks, and nothing +/// changes when it does not. +/// ───────────────────────────────────────────────────────────────────────── +void main() { + setUp(() => ServiceProfile.setActive(ServiceProfile.parcel)); + tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel)); + + /// A CX customer pickup, as the queue returns it. + Map cxPickup() => ApiConfig.pickupFromBooking(const { + 'bookingid': 101, + 'orderid': 'CX-1', + 'pickup_source_type': 'customer', + 'pickup_source_name': 'Anitha R', + }); + + /// A kitchen / counter collection. + Map sourcePickup() => ApiConfig.pickupFromBooking(const { + 'bookingid': 201, + 'orderid': 'KIT-1', + 'pickup_source_type': 'merchant', + 'pickup_source_name': 'Sri Balaji Kitchen', + }); + + /// A row from before these fields existed — the common case today. + Map silentRow() => ApiConfig.pickupFromBooking(const { + 'bookingid': 301, + 'orderid': 'OLD-1', + }); + + group('the row decides, when the row has said something', () { + test('a CX customer pickup captures shipment details', () { + // The five pickups in the scenario. True on BOTH tenants, which is the + // whole change: it is a fact about the job, not about the rider. + for (final line in [ServiceProfile.parcel, ServiceProfile.milkMan]) { + ServiceProfile.setActive(line); + expect( + WorkPolicy.capturesShipmentDetails(cxPickup()), + isTrue, + reason: 'a customer door needs the verify flow on ${line.label}', + ); + } + }); + + test('a kitchen collection does not', () { + // The ten parcels. Also true on both tenants — the manifest is known + // before the rider arrives, whoever he works for. + for (final line in [ServiceProfile.parcel, ServiceProfile.milkMan]) { + ServiceProfile.setActive(line); + expect( + WorkPolicy.capturesShipmentDetails(sourcePickup()), + isFalse, + reason: 'a counter manifest is not itemised at the door', + ); + } + }); + + test('the two coexist in one queue, answering differently', () { + // The regression in one line: fifteen rows, two workflows, one rider. + final queue = [cxPickup(), sourcePickup()]; + expect( + queue.map(WorkPolicy.capturesShipmentDetails).toList(), + [true, false], + reason: + 'this list was [true, true] or [false, false] before — whichever ' + 'the tenant happened to say', + ); + }); + + test('a CX pickup never becomes a source/kitchen task', () { + final profile = TaskProfile.of(cxPickup()); + expect(profile.kind, WorkKind.customerPickup); + expect( + profile.groupsBySource, + isFalse, + reason: 'a customer door is not a counter and must not be grouped ' + 'under one like a kitchen manifest', + ); + }); + }); + + group('the line still decides where the row is silent', () { + // The guard that makes this safe to ship: every existing row and every + // existing fixture takes this path, and must behave exactly as before. + test('a silent row follows the parcel line', () { + ServiceProfile.setActive(ServiceProfile.parcel); + expect( + WorkPolicy.capturesShipmentDetails(silentRow()), + ServiceProfile.parcel.needsVerification, + ); + expect( + WorkPolicy.capturesShipmentAddresses(silentRow()), + ServiceProfile.parcel.capturesShipmentAddresses, + ); + expect( + WorkPolicy.initiatesShipment(silentRow()), + ServiceProfile.parcel.initiatesShipment, + ); + }); + + test('a silent row follows the meal line', () { + ServiceProfile.setActive(ServiceProfile.milkMan); + expect( + WorkPolicy.capturesShipmentDetails(silentRow()), + ServiceProfile.milkMan.needsVerification, + ); + expect( + WorkPolicy.deliversToCustomer(silentRow()), + ServiceProfile.milkMan.deliversToCustomer, + ); + }); + }); + + group('when an accepted stop leaves Home', () { + test('a CX pickup leaves on accept; a kitchen load does not', () { + // The per-item replacement for `handsOffAtCollection`. Accepting a + // customer visit is an appointment the rider now owes somebody, so it + // belongs on Bookings. Accepting a counter load puts nothing in his + // hands — he still has to go and collect it. + expect(WorkPolicy.staysOnHomeUntilCollected(cxPickup()), isFalse); + expect(WorkPolicy.staysOnHomeUntilCollected(sourcePickup()), isTrue); + }); + + test('and the answer does not change with the tenant', () { + // The whole failure this replaces: one answer for every row on the + // screen. On a meal tenant an accepted CX pickup sat on Home beside ten + // kitchen bags with nothing to tell them apart. + for (final line in [ServiceProfile.parcel, ServiceProfile.milkMan]) { + ServiceProfile.setActive(line); + expect( + WorkPolicy.staysOnHomeUntilCollected(cxPickup()), + isFalse, + reason: line.label, + ); + expect( + WorkPolicy.staysOnHomeUntilCollected(sourcePickup()), + isTrue, + reason: line.label, + ); + } + }); + + test('a silent row still follows the line', () { + for (final line in [ServiceProfile.parcel, ServiceProfile.milkMan]) { + ServiceProfile.setActive(line); + expect( + WorkPolicy.staysOnHomeUntilCollected(silentRow()), + line.handsOffAtCollection, + reason: 'existing payloads must behave exactly as before', + ); + } + }); + }); + + group('which surface an item belongs on', () { + test('an accepted CX pickup moves Home → Bookings', () { + // The rule the scenario names: accepting a customer visit makes it an + // appointment the rider owes somebody. + final row = cxPickup(); + expect(WorkPolicy.surfaceOf(row, accepted: false), WorkSurface.home); + expect(WorkPolicy.surfaceOf(row, accepted: true), WorkSurface.bookings); + }); + + test('an accepted kitchen load stays on Home', () { + // Accepting a counter load puts nothing in his hands — he still has to go + // and collect it. + final row = sourcePickup(); + expect(WorkPolicy.surfaceOf(row, accepted: false), WorkSurface.home); + expect( + WorkPolicy.surfaceOf(row, accepted: true), + WorkSurface.home, + reason: 'nothing is in his hands yet', + ); + }); + + test('in-custody work is Deliveries, whichever kind it is', () { + for (final action in const ['deliver', 'start_delivery', 'inward_at_hub']) { + final row = ApiConfig.pickupFromBooking({ + 'bookingid': 401, + 'next_action': action, + }); + expect( + WorkPolicy.surfaceOf(row, accepted: true), + WorkSurface.deliveries, + reason: action, + ); + } + }); + + test('finished work is Activity', () { + final row = ApiConfig.pickupFromBooking(const { + 'bookingid': 501, + 'next_action': 'handed_to_hub', + }); + expect(WorkPolicy.surfaceOf(row, accepted: true), WorkSurface.activity); + }); + }); + + group('a base leg is read, never guessed', () { + test('only an explicit inward_at_hub ends at a base', () { + expect( + WorkPolicy.endsAtHub( + ApiConfig.pickupFromBooking(const { + 'bookingid': 601, + 'next_action': 'inward_at_hub', + }), + ), + isTrue, + ); + }); + + test('a silent row does NOT fall back to the line here', () { + // Deliberately the one method with no fallback. Guessing a base leg from + // the rider's tenant is how a hyperlocal parcel gets routed to a counter + // and sits there. + ServiceProfile.setActive(ServiceProfile.parcel); + expect(ServiceProfile.parcel.endsAtHub, isTrue); + expect( + WorkPolicy.endsAtHub(silentRow()), + isFalse, + reason: 'the line says the day ends at a base; this ROW does not', + ); + }); + + test('a customer delivery does not end at a base', () { + expect( + WorkPolicy.endsAtHub( + ApiConfig.pickupFromBooking(const { + 'bookingid': 701, + 'next_action': 'deliver', + }), + ), + isFalse, + ); + }); + }); + + group('the RS Puram round', () { + test('15 assigned items keep 15 distinct answers, and none vanish', () { + ServiceProfile.setActive(ServiceProfile.milkMan); + + final round = >[ + for (var i = 0; i < 10; i++) + ApiConfig.pickupFromBooking({ + 'bookingid': 1000 + i, + 'orderid': 'KIT-$i', + 'pickup_source_type': 'merchant', + 'pickup_source_name': 'Kitchen 2', + }), + for (var i = 0; i < 5; i++) + ApiConfig.pickupFromBooking({ + 'bookingid': 2000 + i, + 'orderid': 'CX-$i', + 'pickup_source_type': 'customer', + 'pickup_source_name': 'Customer $i', + }), + ]; + + expect(round, hasLength(15), reason: 'nothing may be filtered away'); + + final kitchens = round.where( + (r) => TaskProfile.of(r).kind == WorkKind.sourcePickup, + ); + final customers = round.where( + (r) => TaskProfile.of(r).kind == WorkKind.customerPickup, + ); + expect(kitchens, hasLength(10)); + expect(customers, hasLength(5)); + + // Accepting all fifteen sends the two kinds to two different surfaces. + expect( + kitchens.map((r) => WorkPolicy.surfaceOf(r, accepted: true)).toSet(), + {WorkSurface.home}, + ); + expect( + customers.map((r) => WorkPolicy.surfaceOf(r, accepted: true)).toSet(), + {WorkSurface.bookings}, + ); + + // And only the five customer doors run the verify flow — on a meal + // tenant, where previously none of them would have. + expect( + customers.every(WorkPolicy.capturesShipmentDetails), + isTrue, + ); + expect( + kitchens.any(WorkPolicy.capturesShipmentDetails), + isFalse, + ); + }); + }); +} diff --git a/test/multi_destination_stop_test.dart b/test/multi_destination_stop_test.dart new file mode 100644 index 0000000..24749fb --- /dev/null +++ b/test/multi_destination_stop_test.dart @@ -0,0 +1,193 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:miler/data/api_config.dart'; +import 'package:miler/data/milk_run.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// ONE PICKUP, SEVERAL DOORS +/// +/// A customer-app booking carries N destinations. `GET /miler/bookings` +/// returns ONE ROW PER DESTINATION once the visit has been collected — same +/// `bookingid`, same `bookingreference`, different door, distinguished only by +/// `destinationseq` / `destinationcount`. +/// +/// Everything the device remembers about a stop is keyed on `orderid`: the +/// accepted store dedupes on it (`byId[id] = copy`), the consignment-id map +/// files under it, the collected and out-for-delivery sets hold it. So three +/// doors sharing one `orderid` was not a display bug — two of the three rows +/// were deduped away before any screen saw them, and the two consignment ids +/// that lost the race were unrecoverable. A rider holding three bags could +/// deliver one. +/// +/// These tests pin the two halves of the fix that can silently rot: +/// +/// 1. The **suffix exists only where there is something to tell apart**, so +/// every single-destination booking — which is all of logistics and all of +/// the milk run — keys exactly as it did before. +/// 2. **Collection is per visit, delivery is per door.** The collected set is +/// written once, under the booking key, while the pickup is still a single +/// pre-pickup row; the rows that replace it each carry their own key and +/// must still read as collected. +/// ───────────────────────────────────────────────────────────────────────── +void main() { + /// One row of `GET /miler/bookings` for a three-destination customer pickup, + /// after `pickup-complete` has fanned it out. + Map destinationRow(int seq, {int count = 3}) => { + 'bookingid': 482913, + 'bookingreference': 'DM-482913', + 'status': 'Converted_To_Consignment', + 'destinationseq': seq, + 'destinationcount': count, + 'consignmentid': 500 + seq, + 'trackingno': 'DM-TRK-884102$seq', + 'recipientname': ['Anitha R', 'Suresh K', 'Meena P'][seq], + 'recipientphone': '987654321$seq', + 'deliveryaddress': ['Chennai', 'Ernakulam', 'Bengaluru'][seq], + 'customername': 'Ravi Kumar', // the SENDER — not at any of the three doors + 'pickupaddress': 'Gandhipuram, Coimbatore', + }; + + group('the three doors are three stops', () { + test('each destination gets its own key', () { + final keys = [ + for (var seq = 0; seq < 3; seq++) + MilkRun.idOf(ApiConfig.pickupFromBooking(destinationRow(seq))), + ]; + + expect(keys.toSet(), hasLength(3), reason: 'three doors, three keys'); + expect(keys, ['DM-482913#0', 'DM-482913#1', 'DM-482913#2']); + }); + + test('a store keyed by orderid keeps all three', () { + // Exactly what addAcceptedBookings does: `byId[orderid] = row`. + final byId = >{}; + for (var seq = 0; seq < 3; seq++) { + final stop = ApiConfig.pickupFromBooking(destinationRow(seq)); + byId[MilkRun.idOf(stop)] = stop; + } + expect(byId, hasLength(3), reason: 'none of the three deduped away'); + }); + + test('each keeps its own consignment id', () { + // The map rememberConsignmentId writes into. One slot per door, so no + // door can lose its parcel to whichever wrote last. + final consignments = {}; + for (var seq = 0; seq < 3; seq++) { + final stop = ApiConfig.pickupFromBooking(destinationRow(seq)); + consignments[MilkRun.idOf(stop)] = MilkRun.consignmentIdOf(stop); + } + expect(consignments.values.toSet(), {'500', '501', '502'}); + }); + + test('the booking key is recoverable from any of them', () { + for (var seq = 0; seq < 3; seq++) { + final stop = ApiConfig.pickupFromBooking(destinationRow(seq)); + expect(MilkRun.bookingKeyOf(stop), 'DM-482913'); + expect(stop['bookingreference'], 'DM-482913'); + } + }); + }); + + group('the suffix appears only where it is needed', () { + test('a single-destination booking keys exactly as before', () { + final stop = ApiConfig.pickupFromBooking({ + 'bookingid': 77, + 'bookingreference': 'DM-000077', + 'status': 'Pending_Pickup', + 'destinationseq': 0, + 'destinationcount': 1, + }); + expect(MilkRun.idOf(stop), 'DM-000077'); + expect(MilkRun.bookingKeyOf(stop), 'DM-000077'); + }); + + test('a booking with no destination fields at all is untouched', () { + // Every console booking, and any backend that has not shipped the + // destination fields yet. The absence must cost nothing. + final stop = ApiConfig.pickupFromBooking({ + 'bookingid': 42, + 'bookingreference': 'DM-000042', + 'status': 'Pending_Pickup', + }); + expect(MilkRun.idOf(stop), 'DM-000042'); + expect(MilkRun.destinationCountOf(stop), 1); + expect(MilkRun.stopLabel(stop), isEmpty); + }); + }); + + group('the rider is told which door this is', () { + test('Stop N of M, counting from one', () { + final stop = ApiConfig.pickupFromBooking(destinationRow(1)); + expect(MilkRun.stopLabel(stop), 'Stop 2 of 3'); + }); + + test('says nothing when there is only one door', () { + final stop = ApiConfig.pickupFromBooking(destinationRow(0, count: 1)); + expect( + MilkRun.stopLabel(stop), + isEmpty, + reason: 'a label on every card in the app stops being read', + ); + }); + + test('the receiver has a number of their own to ring', () { + // The card dials `recipientphone` on a delivery leg, not the booking's + // `pickupcontactno` — that one is the SENDER, and ringing him from the + // receiver's gate is what it used to do at all three gates. + final stop = ApiConfig.pickupFromBooking(destinationRow(0)); + expect(stop['recipientphone'], '9876543210'); + expect( + stop['pickupcontactno'], + isNot(stop['recipientphone']), + reason: 'sender and receiver are different people', + ); + }); + + test('the receiver and the tracking number survive the adapter', () { + // The adapter builds a fixed map, so a field it does not name is a field + // the UI can never see. These three were unnamed until now, which put the + // SENDER's name over the receiver's door. + final stop = ApiConfig.pickupFromBooking(destinationRow(2)); + expect(stop['recipientname'], 'Meena P'); + expect(stop['recipientphone'], '987654321 2'.replaceAll(' ', '')); + expect(stop['trackingno'], 'DM-TRK-8841022'); + expect( + stop['pickupcustomer'], + 'Ravi Kumar', + reason: 'the sender is still carried, just no longer as the drop', + ); + }); + }); + + group('collection is per visit, delivery is per door', () { + test('a collected visit keeps its doors collected after the split', () { + // The rider collected the pickup while it was still ONE row, so this is + // what the collected set holds. Nothing in it mentions a destination. + final collected = {'DM-482913'}; + + for (var seq = 0; seq < 3; seq++) { + final stop = ApiConfig.pickupFromBooking(destinationRow(seq)); + expect( + MilkRun.wasCollected(stop, collected), + isTrue, + reason: + 'door $seq must not drop back to "not collected" at the moment ' + 'the rider actually has the bag in his hands', + ); + } + }); + + test('a door the rider has not collected still reads as uncollected', () { + final stop = ApiConfig.pickupFromBooking(destinationRow(0)); + expect(MilkRun.wasCollected(stop, {'DM-999999'}), isFalse); + expect(MilkRun.wasCollected(stop, const {}), isFalse); + }); + + test('a per-door key in the set also counts', () { + // Once the delivery half starts writing per-door keys, both spellings + // have to answer — the set can hold either during a single shift. + final stop = ApiConfig.pickupFromBooking(destinationRow(1)); + expect(MilkRun.wasCollected(stop, {'DM-482913#1'}), isTrue); + }); + }); +} diff --git a/test/multidrop_shot_test.dart b/test/multidrop_shot_test.dart new file mode 100644 index 0000000..b8dc487 --- /dev/null +++ b/test/multidrop_shot_test.dart @@ -0,0 +1,170 @@ +@Tags(['shots']) +library; + +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:get/get.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'support/golden_fonts.dart'; + +import 'package:miler/data/api_config.dart'; +import 'package:miler/data/mock_backend.dart'; +import 'package:miler/data/service_profile.dart'; +import 'package:miler/views/Dashboard/pickups/pickups.dart'; +import 'package:miler/views/helpers/constants/Colorconstants.dart'; + +/// A look at the three-door card, rendered from the SAME fixture the running +/// app serves — `MockBackend.multiDropVisit` — through the SAME adapter the +/// network path uses. Nothing here hand-builds a stop map, so what is on screen +/// is what a rider sees when the backend sends a fanned-out customer pickup. +/// +/// Writes PNGs to `test/shots_out/` rather than comparing goldens: the point is +/// to look at it, not to pin it. +void main() { + setUpAll(() async { + await loadGoldenFonts(); + Directory('test/shots_out').createSync(recursive: true); + }); + + tearDown(() { + Get.reset(); + ServiceProfile.setActive(ServiceProfile.parcel); + }); + + Future shoot( + WidgetTester tester, + String name, + Widget child, { + double height = 340, + double width = 390, + }) async { + SharedPreferences.setMockInitialValues({'userid': 1}); + tester.view.physicalSize = Size(width * 3, height * 3); + tester.view.devicePixelRatio = 3.0; + addTearDown(tester.view.reset); + + await tester.pumpWidget( + ScreenUtilInit( + designSize: Size(width, height), + builder: (_, _) => MaterialApp( + debugShowCheckedModeBanner: false, + home: Scaffold( + backgroundColor: ColorConstants.daylightSurface, + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: child, + ), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + + await expectLater( + find.byType(MaterialApp), + matchesGoldenFile('shots_out/$name.png'), + ); + } + + /// The fixture the app serves, put through the real adapter. + List> stops() => [ + for (final row in MockBackend.multiDropVisit) + ApiConfig.pickupFromBooking(Map.from(row)), + ]; + + testWidgets('door 1 of 3 — Chennai, two parcels', (tester) async { + ServiceProfile.setActive(ServiceProfile.milkMan); + final all = stops(); + await shoot( + tester, + 'multidrop_stop1', + PickupCard( + item: all[0], + displayStep: 1, + distanceMeters: 1400, + collectedIds: {all[0]['orderid'] as String}, + outForDeliveryIds: {all[0]['orderid'] as String}, + ), + ); + }); + + testWidgets('door 2 of 3 — Ernakulam', (tester) async { + ServiceProfile.setActive(ServiceProfile.milkMan); + final all = stops(); + await shoot( + tester, + 'multidrop_stop2', + PickupCard( + item: all[1], + displayStep: 2, + distanceMeters: 3100, + collectedIds: {all[1]['orderid'] as String}, + outForDeliveryIds: {all[1]['orderid'] as String}, + ), + ); + }); + + testWidgets('the three stacked, as the queue shows them', (tester) async { + ServiceProfile.setActive(ServiceProfile.milkMan); + final all = stops(); + await shoot( + tester, + 'multidrop_queue', + Column( + children: [ + for (var i = 0; i < all.length; i++) + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: PickupCard( + item: all[i], + displayStep: i + 1, + distanceMeters: 1400.0 + i * 900, + collectedIds: {all[i]['orderid'] as String}, + outForDeliveryIds: {all[i]['orderid'] as String}, + ), + ), + ], + ), + height: 960, + ); + }); + + testWidgets('a single-destination booking is unchanged', (tester) async { + ServiceProfile.setActive(ServiceProfile.milkMan); + final one = ApiConfig.pickupFromBooking({ + 'bookingid': 900777, + 'bookingreference': 'MOCK-000777', + 'status': 'Converted_To_Consignment', + 'consignmentid': 900777, + 'consignmentstatus': 'Out_for_Delivery', + 'next_action': 'deliver', + 'destinationseq': 0, + 'destinationcount': 1, + 'pickup_source_name': 'Ravi Kumar', + 'pickupaddress': '14, Cross Cut Road, Gandhipuram, Coimbatore 641012', + 'deliveryaddress': '12, SNS Colony, Peelamedu, Coimbatore 641004', + 'customername': 'Ravi Kumar', + 'recipientname': 'Joe Mathew', + 'parcels': [ + {'bookingparcelid': 1, 'itemcategory': 'General'}, + ], + }); + await shoot( + tester, + 'multidrop_single', + PickupCard( + item: one, + displayStep: 1, + distanceMeters: 900, + collectedIds: {one['orderid'] as String}, + outForDeliveryIds: {one['orderid'] as String}, + ), + ); + }); +} diff --git a/test/offline_banner_test.dart b/test/offline_banner_test.dart index a08adec..26383c2 100644 --- a/test/offline_banner_test.dart +++ b/test/offline_banner_test.dart @@ -69,7 +69,7 @@ void main() { expect(tester.getSize(find.byType(OfflineBanner)).height, lessThan(64)); }); - testWidgets('it says what happens next, not just what is wrong', ( + testWidgets('it says what happens next, and promises nothing it cannot do', ( tester, ) async { await pump(tester, offline: true); @@ -83,7 +83,28 @@ void main() { .first .data!; expect(text, contains('Offline')); - expect(text.toLowerCase(), contains('sync')); + + // ── This used to assert the opposite ── + // + // It required the word "sync", and the banner duly said "Changes will sync + // when you're back online". **There is no mutation queue in this app** — no + // outbox, no replay, no retry loop. A status the rider set with no signal + // was attempted once, failed, and was never sent again. The banner was + // promising a mechanism that does not exist, to the one person who would be + // blamed when the hub had no record of the stop. + // + // If a queue is ever built, this assertion is the place to reverse — and + // the copy may promise a sync again on the day something performs one. + expect( + text.toLowerCase(), + isNot(contains('sync')), + reason: 'nothing in this app replays a failed mutation', + ); + expect( + text.toLowerCase(), + contains('connection'), + reason: 'it must still say what he needs, not only that he is offline', + ); }); testWidgets('online, it occupies no space at all', (tester) async { diff --git a/test/parcel_photos_test.dart b/test/parcel_photos_test.dart new file mode 100644 index 0000000..d6f3fa8 --- /dev/null +++ b/test/parcel_photos_test.dart @@ -0,0 +1,213 @@ +import 'dart:convert'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:http/testing.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:miler/data/miler_api.dart'; +import 'package:miler/providers/pickuplog/pickuplog_provider.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// THE DOOR PHOTOGRAPH REACHES THE HUB +/// +/// `POST /miler/bookings/:id/parcel` has accepted `photos` for as long as the +/// route has existed. The server's own note says why it is there: *"Weight +/// without a photograph is a number the customer has no way to check, and this +/// is the only point in the flow where anyone is standing next to the parcel."* +/// +/// The app never sent it — and not by oversight in one place, but through three +/// separate defects that each had to be fixed: +/// +/// 1. `ParcelEntry.toJson()` had no `photos` key at all. +/// 2. `uploadProof` kept the public **URL** from the sign response and threw +/// away the **key**, which is what `photos` is specified in. +/// 3. The pickup upload passed a **booking** id in the `consignmentid` slot, +/// so every pickup proof this app ever uploaded was filed under a +/// consignment number that did not exist. +/// +/// Meanwhile `StopVerificationPage` *compels* the photograph — the rider cannot +/// confirm the stop without one. So the app demanded the single piece of +/// evidence that settles a dispute, uploaded it, and dropped the reference. +/// ───────────────────────────────────────────────────────────────────────── +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + group('ParcelEntry serialisation', () { + test('photos are sent when there are any', () { + final json = const ParcelEntry( + weight: 2.5, + photos: ['pickup_proof/pickup_proof-4821-20260916-101402-a1b2.jpg'], + ).toJson(); + + expect(json['photos'], isA()); + expect( + json['photos'], + ['pickup_proof/pickup_proof-4821-20260916-101402-a1b2.jpg'], + ); + expect(json['weight'], 2.5); + }); + + test('the key is a storage key, never a URL', () { + // The customer is served a short-lived signed link *derived from* the + // key. A URL stored here either expires or, worse, never does. + final json = const ParcelEntry( + weight: 1, + photos: ['pickup_proof/pickup_proof-4821-20260916-101402-a1b2.jpg'], + ).toJson(); + final photo = (json['photos'] as List).single.toString(); + + expect(photo, isNot(startsWith('http'))); + expect(photo, isNot(contains('://'))); + expect(photo, contains('/'), reason: 'a key is {folder}/{file}'); + }); + + test('no photo means the field is OMITTED, not sent empty', () { + // An empty array is a claim that no photograph was taken. A failed + // upload is not that claim, and the difference matters to whoever reads + // the record afterwards. + final json = const ParcelEntry(weight: 1).toJson(); + expect(json.containsKey('photos'), isFalse); + }); + + test('a local filesystem path is never what gets sent', () { + // The shape the bug produced: `parcelImage` held `/data/user/0/…/x.jpg` + // and nothing ever turned it into a reference the server could resolve. + final json = const ParcelEntry(weight: 1).toJson(); + expect(json.toString(), isNot(contains('/data/user/'))); + expect(json.toString(), isNot(contains('.jpg'))); + }); + }); + + group('the parcel request that actually leaves the phone', () { + late List sent; + + setUp(() { + SharedPreferences.setMockInitialValues({'userid': 38, 'authtoken': 't'}); + sent = []; + MilerApi.client = MockClient((req) async { + sent.add(req); + return http.Response('{"success":true,"data":{}}', 200); + }); + }); + + tearDown(() => MilerApi.client = http.Client()); + + Map bodyOf(http.Request r) => + jsonDecode(r.body) as Map; + + test('photos ride on the wire to /parcel', () async { + await MilerApi.submitParcels(4821, const [ + ParcelEntry(weight: 2, photos: ['pickup_proof/p-4821-x.jpg']), + ParcelEntry(weight: 2), + ]); + + expect(sent, hasLength(1)); + expect(sent.single.url.path, endsWith('/miler/bookings/4821/parcel')); + + final parcels = bodyOf(sent.single)['parcels'] as List; + expect(parcels, hasLength(2)); + expect((parcels[0] as Map)['photos'], ['pickup_proof/p-4821-x.jpg']); + expect( + (parcels[1] as Map).containsKey('photos'), + isFalse, + reason: 'one photograph of the load, not one claimed per box', + ); + }); + + test('the sign call keys a PRE-pickup proof on the BOOKING', () async { + // There is no consignment yet — `pickup-complete` has not run. The server + // builds the storage key from consignmentid, then bookingid, then the + // rider; passing a booking id in the consignment slot filed the proof + // under a consignment number that did not exist. + await MilerApi.signUpload( + purpose: MilerApi.proofPickup, + bookingId: 4821, + ); + + final body = bodyOf(sent.single); + expect(body['bookingid'], 4821); + expect( + body.containsKey('consignmentid'), + isFalse, + reason: 'a booking id must never be sent as a consignment id', + ); + expect(body['purpose'], 'pickup_proof'); + }); + + test('a delivery proof still keys on the consignment', () async { + // The other half of the contract, unchanged — `deliver` takes a URL and + // its proof belongs to a consignment that exists by then. + await MilerApi.signUpload( + purpose: MilerApi.proofDelivery, + consignmentId: 77, + ); + + final body = bodyOf(sent.single); + expect(body['consignmentid'], 77); + expect(body.containsKey('bookingid'), isFalse); + }); + }); + + group('the provider turns a verification map into parcels', () { + late List sent; + + setUp(() { + SharedPreferences.setMockInitialValues({'userid': 38, 'authtoken': 't'}); + sent = []; + MilerApi.client = MockClient((req) async { + sent.add(req); + return http.Response('{"success":true,"data":{}}', 200); + }); + }); + + tearDown(() => MilerApi.client = http.Client()); + + test('a key on the verification map reaches the first parcel', () async { + await UpdatePickupProvider().submitParcels(4821, const { + 'pickup': { + 'collected': 3, + 'weight': '6', + 'photos': ['pickup_proof/p-4821-x.jpg'], + }, + }); + + final parcels = + (jsonDecode(sent.single.body) as Map)['parcels'] as List; + expect(parcels, hasLength(3), reason: 'three boxes were collected'); + expect((parcels[0] as Map)['photos'], ['pickup_proof/p-4821-x.jpg']); + expect((parcels[1] as Map).containsKey('photos'), isFalse); + expect((parcels[2] as Map).containsKey('photos'), isFalse); + + // The weight is still split evenly across the three — unchanged. + expect((parcels[0] as Map)['weight'], 2.0); + }); + + test('a failed upload still records the parcels', () async { + // A dead object store must not strand a rider at a door holding parcels + // he cannot record. The pickup goes through; the gap is logged. + await UpdatePickupProvider().submitParcels(4821, const { + 'pickup': {'collected': 1, 'weight': '2'}, + }); + + expect(sent, hasLength(1), reason: 'the parcels were still submitted'); + final parcels = + (jsonDecode(sent.single.body) as Map)['parcels'] as List; + expect((parcels[0] as Map).containsKey('photos'), isFalse); + }); + + test('a blank key is dropped rather than sent', () async { + await UpdatePickupProvider().submitParcels(4821, const { + 'pickup': { + 'collected': 1, + 'weight': '2', + 'photos': ['', ' '], + }, + }); + + final parcels = + (jsonDecode(sent.single.body) as Map)['parcels'] as List; + expect((parcels[0] as Map).containsKey('photos'), isFalse); + }); + }); +} diff --git a/test/set_pin_flow_test.dart b/test/set_pin_flow_test.dart new file mode 100644 index 0000000..5057b53 --- /dev/null +++ b/test/set_pin_flow_test.dart @@ -0,0 +1,386 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:http/testing.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'package:miler/data/api_config.dart'; +import 'package:miler/data/miler_api.dart'; + +/// ───────────────────────────────────────────────────────────────────────── +/// SELF-SET PIN ON FIRST SIGN-IN +/// +/// The console no longer issues a rider a PIN. `POST /miler/login` answers +/// `pin_set`, and that one boolean decides which screen he is owed: +/// +/// pin_set: false → Set-PIN → POST /miler/set-pin → logged in +/// pin_set: true → Enter-PIN → POST /miler/verify-pin → logged in +/// +/// ── What this replaces, and why it was a dead end ── +/// +/// The app read only the **status code** of `/miler/login` and threw the body +/// away. From "an account exists" it inferred Enter-PIN; from "it does not" it +/// inferred an OTP branch that verified nothing — `verifyOtp` returned `true` +/// without checking a digit — and ended at a Create-MPIN screen whose save +/// button called `updatePin`, which had no route behind it and returned a +/// manufactured `403 "Your MPIN is issued by your office and cannot be changed +/// from the app."` +/// +/// So a rider with no PIN could not sign in by any path, and the three seeded +/// test riders (`8000000001`–`3`) were unreachable. +/// +/// ── The rule these tests exist to hold ── +/// +/// **Branch on the boolean, never on the message.** `"PIN verification +/// required"` is prose and prose gets reworded; the day it does, a +/// string-matching client sends every rider to the wrong screen. +/// ───────────────────────────────────────────────────────────────────────── +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late List sent; + + /// Installs a fake server. [handler] answers by path. + void serve(http.Response Function(http.Request req) handler) { + sent = []; + MilerApi.client = MockClient((req) async { + sent.add(req); + return handler(req); + }); + } + + setUp(() => SharedPreferences.setMockInitialValues({})); + tearDown(() => MilerApi.client = http.Client()); + + Map bodyOf(http.Request r) => + jsonDecode(r.body) as Map; + + /// The session envelope `set-pin` and `verify-pin` both answer with. + String sessionBody({int userId = 46}) => jsonEncode({ + 'success': true, + 'token': 'jwt-for-$userId', + 'tenantid': 1, + 'tenantname': 'Doormile Coimbatore Logistics', + 'user': { + 'userid': userId, + 'authname': 'Seed Rider', + 'contactno': '8000000001', + 'profile': { + 'userid': userId, + 'displayname': 'Seed Rider', + 'phone': '8000000001', + 'availabilitystatus': 'Offline', + }, + }, + }); + + group('pin_set decides the screen', () { + test('pin_set:false → the rider must set one', () async { + serve( + (_) => http.Response( + jsonEncode({ + 'success': true, + 'message': 'PIN verification required', + 'phone': '8000000001', + 'pin_set': false, + }), + 200, + ), + ); + + final res = await MilerApi.login('8000000001'); + expect(res.ok, isTrue); + expect(MilerApi.pinSetOf(res), isFalse); + }); + + test('pin_set:true → the existing Enter-PIN path', () async { + serve( + (_) => http.Response( + jsonEncode({ + 'success': true, + 'message': 'PIN verification required', + 'phone': '9000000009', + 'pin_set': true, + }), + 200, + ), + ); + + final res = await MilerApi.login('9000000009'); + expect(MilerApi.pinSetOf(res), isTrue); + }); + + test('the MESSAGE is identical in both cases — only the flag differs', () { + // The reason this is a boolean and not a string match. Both responses + // carry "PIN verification required"; a client reading the sentence cannot + // tell a first-time rider from a returning one. + const first = '{"success":true,"message":"PIN verification required",' + '"pin_set":false}'; + const returning = '{"success":true,"message":"PIN verification required",' + '"pin_set":true}'; + expect( + jsonDecode(first)['message'], + jsonDecode(returning)['message'], + ); + expect( + jsonDecode(first)['pin_set'], + isNot(jsonDecode(returning)['pin_set']), + ); + }); + + test('an absent pin_set is null, and callers fall back to Enter-PIN', () async { + // An older server, or a body that did not carry the field. Enter-PIN is + // the safe direction: a rider who HAS a PIN can sign in, and one who does + // not gets a refusal he can report — rather than a Set-PIN screen that + // will 409 and strand him. + serve((_) => http.Response(jsonEncode({'success': true}), 200)); + final res = await MilerApi.login('8000000001'); + expect(MilerApi.pinSetOf(res), isNull); + }); + + test('a string "false" is read as false, not as truthy prose', () async { + serve( + (_) => http.Response( + jsonEncode({'success': true, 'pin_set': 'false'}), + 200, + ), + ); + expect(MilerApi.pinSetOf(await MilerApi.login('8000000001')), isFalse); + }); + }); + + group('POST /miler/set-pin', () { + test('sends the contract body and stores the session', () async { + serve((req) => http.Response(sessionBody(), 200)); + + final res = await MilerApi.setPin( + phone: '8000000001', + pin: '4271', + deviceToken: 'fcm-token-abc', + ); + + expect(res.ok, isTrue); + expect(sent.single.url.path, endsWith('/miler/set-pin')); + + final body = bodyOf(sent.single); + expect(body['phone'], '8000000001'); + expect(body['new_pin'], '4271'); + expect(body['configid'], MilerApi.configId); + expect(body['device_token'], 'fcm-token-abc'); + expect( + body.containsKey('pin'), + isFalse, + reason: 'the field is new_pin on this route, not pin', + ); + + // The whole point: the rider is signed in by this one call. + expect(await ApiConfig.getToken(), 'jwt-for-46'); + }); + + test('the PIN goes as a STRING, so a leading zero survives', () async { + // The backend bcrypt-compares the PIN as text. Round-tripping "0512" + // through an int gives 512, and a valid PIN is refused forever. + serve((req) => http.Response(sessionBody(), 200)); + await MilerApi.setPin(phone: '8000000001', pin: '0512'); + expect(bodyOf(sent.single)['new_pin'], '0512'); + }); + + test('device_token is omitted rather than sent empty', () async { + serve((req) => http.Response(sessionBody(), 200)); + await MilerApi.setPin(phone: '8000000001', pin: '4271'); + expect(bodyOf(sent.single).containsKey('device_token'), isFalse); + }); + + test('409 — a PIN already exists, and no session is minted', () async { + serve( + (_) => http.Response( + jsonEncode({ + 'success': false, + 'message': 'PIN already set for this account', + }), + 409, + ), + ); + + final res = await MilerApi.setPin(phone: '8000000001', pin: '4271'); + expect(res.ok, isFalse); + expect(res.status, 409); + expect( + await ApiConfig.getToken(), + isNull, + reason: 'a refused set-pin must never leave a token behind', + ); + }); + + test('404 — the number is not registered', () async { + serve( + (_) => http.Response( + jsonEncode({'success': false, 'message': 'no miler account'}), + 404, + ), + ); + final res = await MilerApi.setPin(phone: '7000000000', pin: '4271'); + expect(res.status, 404); + expect(res.ok, isFalse); + }); + + test('403 — the account is not an active miler', () async { + serve( + (_) => http.Response( + jsonEncode({'success': false, 'message': 'inactive'}), + 403, + ), + ); + final res = await MilerApi.setPin(phone: '8000000001', pin: '4271'); + expect(res.status, 403); + expect(res.ok, isFalse); + }); + + test('a 200 with success:false is still a failure', () async { + // The envelope rule: the status line alone is not the answer. + serve( + (_) => http.Response( + jsonEncode({'success': false, 'message': 'nope'}), + 200, + ), + ); + final res = await MilerApi.setPin(phone: '8000000001', pin: '4271'); + expect(res.ok, isFalse); + expect(await ApiConfig.getToken(), isNull); + }); + + test('a success carrying no token leaves no session', () async { + // The credentials were accepted and there is nothing to sign in with. + // An integration fault — and it must not read as a stored session. + serve( + (_) => http.Response(jsonEncode({'success': true}), 200), + ); + final res = await MilerApi.setPin(phone: '8000000001', pin: '4271'); + expect(res.ok, isTrue); + expect(await ApiConfig.getToken(), isNull); + }); + }); + + group('the returning rider is untouched', () { + test('verify-pin still sends `pin` to its own route', () async { + serve((req) => http.Response(sessionBody(userId: 38), 200)); + + await MilerApi.verifyPin(phone: '9000000009', pin: '1234'); + + expect(sent.single.url.path, endsWith('/miler/verify-pin')); + final body = bodyOf(sent.single); + expect(body['pin'], '1234'); + expect( + body.containsKey('new_pin'), + isFalse, + reason: 'new_pin belongs to set-pin alone', + ); + expect(await ApiConfig.getToken(), 'jwt-for-38'); + }); + + test('the two routes are genuinely different endpoints', () async { + serve((req) => http.Response(sessionBody(), 200)); + await MilerApi.setPin(phone: '8000000001', pin: '4271'); + await MilerApi.verifyPin(phone: '8000000001', pin: '4271'); + + expect(sent, hasLength(2)); + expect(sent[0].url.path, endsWith('/miler/set-pin')); + expect(sent[1].url.path, endsWith('/miler/verify-pin')); + }); + }); + + group('the seeded riders', () { + // The three created with no PIN, for exactly this flow. Before this change + // none of them could sign in: precheck read "account exists" and sent them + // to Enter-PIN, where no PIN they typed would ever match. + for (final phone in const ['8000000001', '8000000002', '8000000003']) { + test('$phone is routed to Set-PIN and signs in', () async { + serve((req) { + if (req.url.path.endsWith('/miler/login')) { + return http.Response( + jsonEncode({ + 'success': true, + 'message': 'PIN verification required', + 'phone': phone, + 'pin_set': false, + }), + 200, + ); + } + return http.Response(sessionBody(), 200); + }); + + final login = await MilerApi.login(phone); + expect( + MilerApi.pinSetOf(login), + isFalse, + reason: 'seeded riders are created without a PIN', + ); + + final set = await MilerApi.setPin(phone: phone, pin: '4271'); + expect(set.ok, isTrue); + expect(await ApiConfig.getToken(), isNotNull); + + expect(sent, hasLength(2)); + expect(sent[0].url.path, endsWith('/miler/login')); + expect(sent[1].url.path, endsWith('/miler/set-pin')); + }); + } + }); + + group('no master PIN and no dead OTP path survive', () { + /// Source with every comment line stripped. + /// + /// These assertions are about **code**, not prose — and the distinction is + /// not academic: the comments that explain why the master-PIN constants + /// were removed necessarily quote their names, and a naive scan matches its + /// own explanation and fails. Strip the commentary, assert the code. + String codeOf(String path) => File(path) + .readAsLinesSync() + .where((l) => !l.trimLeft().startsWith('//')) + .join('\n'); + + final auth = codeOf('lib/controllers/auth.dart'); + final signIn = codeOf('lib/views/onboardscreens/Sign_in.dart'); + + test('the 1234 master-PIN constants are gone', () { + // `_masterPinValue = '1234'`, `masterPinValue` and + // `forceMasterPinPrefKey` were declared on AuthController and read by + // nothing — leftovers of a removed feature. A public constant named + // `masterPinValue` holding four digits reads as a back door whether or + // not anything calls it, and it collided with a PIN a rider could now + // legitimately choose. + expect(auth, isNot(contains('masterPinValue'))); + expect(auth, isNot(contains('forceMasterPinPrefKey'))); + expect(auth, isNot(contains('_forceMasterPinFlow'))); + }); + + test('the office-issues-your-PIN dead end is gone', () { + // The sentence the Create-MPIN screen used to end at, because there was + // no route behind its save button. There is one now. + expect( + auth, + isNot(contains('cannot be changed from the app')), + reason: 'riders set their own PIN on first sign-in', + ); + }); + + test('sign-in no longer routes anyone to the unverified OTP screen', () { + // `verifyOtp` returned true without checking a digit, and the screen it + // led to could not write a PIN. Nothing may route there. + expect(signIn, isNot(contains('OtpPage'))); + expect(signIn, contains('AuthNext.setPin')); + expect(signIn, contains('AuthNext.verifyPin')); + }); + + test('sign-in branches on the enum, never on response prose', () { + expect( + signIn, + isNot(contains('PIN verification required')), + reason: 'the message is prose and prose gets reworded', + ); + }); + }); +} diff --git a/test/shots_out/multidrop_queue.png b/test/shots_out/multidrop_queue.png new file mode 100644 index 0000000000000000000000000000000000000000..ae71c3e01373e6a33bd135354ffa435df905ea9c GIT binary patch literal 231689 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

4lpmg4VSuX(dQ--L0 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

  • s#-nW~Xn1!@QZd-VYJPux8UuKADEbbG)j76g`HoD!?E>r_wFQIu-41jJjc*G zPUgQxA9eK8V1M^U*{z*6I7aKAw;Q{l>-Z(kXW1YjwQ6Vig6;tZ!W# zDH%4ut^{!$9RzXd!+cDGHpI>>TcLzJ|S z?Ke|yr|{}2GrQ5-|L*&2wHIg};u0V!Xc>WS%I;4qvZJS)+cwV1f`dYdBlf^>w9KmV z_4%3`C3OrODZA?t*^&D?sCgyY$8O#?tKU)Y5G&>%6cnOcD=$Q{hLX)jraAcr?)18? zr1M3+&F>dI{XG|5f^_&Bvk_)lQe*DbJ7k=S9r_ZgV2KN!if%_G)$sVg- zl`xCl!)rXkN0%h)tcx8NbCkSwt&JqlKbwWMxsJJv=f+CmI2AiIx-MKL0p&gk~xxoRqg*2TxqA}}q0Z3JNEnvC|Roh~ZobHhXi zdtF3~rCTy!aQ6n^^+X(QU1|HaGV(jyV7b77;XeIlh%D5Uh9N+^&6s>z`h?<}Rb+@c z&^Z$ax2%8t}7D$tPKvgF9E_jGH*!Oq@ zoh-n^VA#O{Tq+M`+Uqu$2yM0~u7%cOX~hrZuKCxqGs-isS+I)<1ULiEey?vHAYRz| z2rp7#pPM4`+TMU4g$hug?s&O`=;3n_kg0Ud>6i!iy7Vo0_?f08509;TF7otwdTxOJ z1IYS|U^YYZfLr7RGm^03VU4JLU$EcW)qOgz@Y&9*(bWFt*G^Fw8o})j)Vze-%JPTr zEYVcbGC=A*U+ZzX@NvYv|-I=%uH zLv^0p&pBk)YEai>VAh{5 zI0!0#%V!#PEp+MI3#M`k^2-S+YHiJnxa{BpzV?CR=yM}-u4hiBq_q?SB9=o=Mzyz1 zd5gf<%v-qe3~ZPy%6B@pTM?A%9f9uF(jpySK#SkpO3R3m}~Z z#kTpD!rzsEaK(ERmuo{njvaxnMa_A1WMsjCOxyp(Z(UFiEH5O%mb?hHw-KWi3q1O$ ze_lPd6>wH{sFr5(33rS`{yR2>gFQL-B!_E8Y zBjr6I=`@Di6nd4qU#fbxuQLSYY?#6H7nCphNkOyH=x!or5R{Y_$_8Z4{2jlLsi3}lSSJ(x=u9cd~>DCGs;#o*b;5Ix;XaibOALpiq~$OV8u9? za-+OAC(7`}tZjte>*5!Lb98!%payghARxGUu%>byRc;0|QHasWkdc-kAc)|4lTWY^ zE9rb%%@zViB1APqnv{1#9=Xm?1D(+L6VLwi^@EUf+a-wbP!nE^1VZXcOXvnVP6Ozs z*(AK+29h6vLOI=7ibbhNXIM33pFzWIN74Z(mHqvioLSlNiEMSOq+4U%956JX7L`Cb zjhE|3x7ujF9@U)!eFJJ5h+pkxV|}pp;cthS*=SOq+h1 zwFOQ#x~CZQWa&mH&laGZeBhW|w#9}zd8tK;JS+TR@J zLG6N>*5=O-xrso{w(W4y+H9(|8Ynihg$(Xm^wE#SP`Lyb+P@rWbzmMv<`|4vgt!gj zfVGP5Qruy>M8Io!Rq6E)2KI|1E4AS7FRB4yA&GD3@=H_G1>R8dtoy!ROP$Sa-4wHn8PV%X;P9)yqYHBS8ZtMDxk>=c>}_D#3QV-f zs*II7N4VFMO7XqMTe&f@So%g?aeEQAjuWgKgPlNP0od5VC82oT$=pH{l^ z^tLI}_;;fV#{eZ_wUA>6lGWk<5!H8qnAtlzYT28bJEUAz&G(#e7M2wAPQn8>Iu%eU z9}$t}Ou{WfK|HX2Mg?Tjrf@QbPpBe^k}=AqsX1nZBdS$Vhh4H1P&xheFnl`g@4cRH zz$RIEBFYVYWfx5|qPGySA*nKUUaQo@QRETS21lBJZ5o8U_juC8RT+m;WTol(Hw11X=n~{LdA_b#`il8r>s}Iw!EpgpVVBCxX7>rjS?>qtD-lLqNkq482Ns+OZB^gvD*4PTs4)_5B--_EGrs zHqPtUuiHS%l2fo|Kq&GH1$Cz_=gYf7(Lt zBhyX-MxW}5eC)B$SBZTeZD?b zuK=y{%r+97t;Px2?o~96J#qLUsFt=9He20+2`U`pNnh|MQJSoJSvCpbH_wG{#D+F^ z%jWrN5UpZh&yJH(nBjMJ7C-hUWv?IpGy~doUF&zNg6&_| zkIoLG2Jgm3^$0JnOoH>T5Bdh9->hFnB1W5V(P3Q;Zby0xxzSgC0W1w!A@A(Hdtv(6 z0^ogj8jAz$CcgYV`OE0qb-q2Y$z~q?fJ3@CkeV{4 ztLc^LYmag!k5sO+(uauZnU zd-xa2aXhi8B3_fUd_C*nxB(1jv-CgfV6RDW`Xe%Zb#bqV@dLfmr9R-5&wNNl)|_;7 zK|Zn3W_AC;pH3hGN&;IJ@+-Vh6?~Hj)DsMc&b#UHBs0`s8iXHy8)ba)d$i=tro$iW z_~)2AWl#DQnf~p;eE6(`L-BKWbCJ3E{UL$k86P3N)i2I071U)}mfk=y3A>OUBx)n@QaI zDq~$YB>roIid@_~CTQ#7tAmSHZCnaBs=a^Y1KxP0kwHT%itLl%9N@OI#LMLkCi9uyHE=R1nyrRg7UoM{$}Om6@SiVIV%h36#~Mq3m!eQry_qh z4}N3T;I_NWnm+nNl~r0cZeHO=!f9{yetP|GWYZ+bL|9!h3s?^Vg2rFT6(&0M*Y})Fvwdh3T}`b~Ma{&Gq`-ggz!?6_y@#-q+a0D& zw50fRB=E4Tp3n_=u2JuQDwSMe(2H95^jk|Po@s=Z}sI4{(7EFL}C?|z|fOVsr|XisDg;-_XLMx zhlBhbnLAUoM!T^TB=<}5Gr_tBH{r-|#ds7l4QF^*qa|qp_4uNU?9<#?(NR{@*oAOh z>duY3!>Y?E7$v1F1?c{y_?5UwqnJw_z%=Q_aB^25#XG`La1fEGr`)T>s7ZoxXyRJwZAp)xqs~7E!alFb+pMXw7XVLV75+ zPg}$`epceDQXGruut42>VL-jdnvRF9^$~vA{!#!w+;L)kt{%Lxpt`j5F4tX*M=xB1 zW!cNP6f`&pABpq$r>2tHm@~HAps>ULWV^HZ{^BndnUxPpY3P#dhG2I zL%RT4@HWXNKqK?`+>pX&LREM|&6~gHti`+%l_?KKsf^HY(&uXVgs5Zq zohsUdD=VT#ay0VbK}(&PkN=( zN~`=EsMMLw{9T$<<3B1Ay;u@)?fA+o@?iJxn#mk6Mm9donqILJys{nnkT2ebD%DT4 zdjPpWVoaW3Lp5>S)ZN!R3pu=_lwxU>``}X#)ZB`V=q=4%C7DuR$7WF;*>z1Y6-DuF zp92;C;$rwt)Ydfx`)%ROLg|oz5ncPo%tGQ+8**b6ByX9qg0FhMcb_}CKO z48|Wv8gVCrBXFwKOjf-(YEwM7&6&)x*UoApa1c%9mS4NRMtWVlRzU0dtTsF%j^#tg z$G36)8_(&=89Sb7{|~0VGOEhvdm9C%k?wBkknWO{l1>Tf?(Xi8kd*F{ZUqUY1*A(l z4qb)MmR*Jy+DXYoFL-Utju1x8!o5;04!O%=lSMj|Yx zvV(YIW7`oifeegG+p~4>*HGZFCzo9^H32tmVo>VR;p%W9`434|^wt(zJdKzZvZU-u zH_#7h(3`rQgwfs`2Cw;{z>VHdDd}V3wrN3t{M?;JU|-C6md&34$6%(R+LL9tYI=1Tp1 zrOeQ1NbnFE4i26^ZBJ2#tOD(eS71&=A6l`Q+SlCDCpo9lLc!ELPv;ZD>OLn6y>otT z75>+~(v3*fnL~SATjBss0DLlPtgqfli87<)Yk^5%N#^5xUCNe-y-J;lIusta3C|B2 zNtI*r4JaD-)t)R)6@=s$scKhJ<%=azQwY%^n=zVSr2Pna#z!^85nTA5 zIL@4cn#_`z@R%*BoWhrkwigUxOekN&{7ec_cNc_{*NFR1zE#i}TO2Sdj=Zp)5~g2% z6*!O?8~J;h3!~OC%#5FQSgS1PDW1w__M8Aq_L;?PgS(>}#1On7P_q#K$JqeL{2jpwz36xlYyA4ssF~k^Ob~i zS>jJ#ffYDYh9e+m(T10LnfC>MdvD}F86ulP>op(NKt3IL0fwW_w4w(SJyPz5FT9?>n;F8Olmr+@NQ5Zh zvFJ^Uf;KsV`v1*$1h#FCAPM-UvG^qTaTn_ z!0b&=CR@qJQj(XTiC!Y5v(C$=uH)i)BM*Hu12k$rHY`LnVOb|~PS#^L`6dBkG394b z&`N|u)_Q|-I1 zZ#@fv!Qh%&lYVdTJD8measlG&+g(jCL`EAo0ViZI9Z?&&*l@!!$O6eS8&t%)fq{|K zu|vX6&*@UD)dWzUnyEEsYqeo&hsT$Zye;~;xox0RD+UL{@%60HkWoH$GoT78xtFZ< zO^BQ=vCvV#;PZ=PGDAJ{9vjT~f&po9bI zm+Xm!Msw1NtAowa|JHxdM7m2q*eXO=dO7iZLdhHf-q&H<`JY$B zSt*5{3e=Xf0394=+FbE(DhGh^X*cs5e@LGBKP*6@8$M3b`-HI~kwTdy>J4m*`eSbM zC}nk{9@T|(nct;BXRl=*tOEbdruuB4Qgs zd_QgU``>j!^;mN{v-u0#sV_;8t_W&s=5|75y60B#qK5+QibT5WPiKd!|Cn>lRH@hGY3!x%t2~*L0XBNJ z<&ms%sT1etV~(VvkuWhDe5IRr*;$O2kPAOazId++YZdGZiCo;p-y=psl~7c zr^3tNd81D~;sYD3)Y?G^+4DvTnP5N(puAm1^888T$FP0>dY<(bCk`-e z+R@$L1){r7uj|_5$oi8;q&Bf{41Jl+-gzJ;8m$ROaI|3SFh?N8hkS7(ry0z8A?_!h zYRVS>)C{;711)-*wZuEwE#0`lC@NOtKdqH|t?PE9o=qJ+o&R&5WSdjfaj0Q{@sniR z??>j_oI1jn-`mfUS}#!pvw&dCs69!ci3ezVk-l)=yGvK1u!QyR*tY${&eT8_OQL|2B9w0s{2OXwo|W|XOt(`J z;^g`7MYe-aUJpDs&TfMwB)DUjUu-eb;6i1lHt<)LVoOwz3W})46Mv3E>`h%qH>>9KdjA8y&RSK z4Kpf(WKc!B7_0a`SJJ{bNb^SEVUh!Po|clbJI(tvrhLr)ELp!%5C@L{a_%8#mNDS# zNMyI7>K$`pmVK9k68Dbz1Enl1604q?09?6v5PkmrWc-)j$@p*R1Z$V;V0#+y(?as~ zz$Zi@QND!J?hljBIkT$;hFVzp%c(h8`&;Yx{nC6&)c2XGe;p0cA|t5{pKh*^eTUE-MQaFM?-eBmUR*i0#zxLKJ-w}YOWU3Qm&zoIRB7mEkVMiKtfd+&3eLoh_j~c|%T>wUe`lwj zgMHy!`Dys;Q)Ux2>S|wihKyw{;(T~1JX`3C*|Wov*~`;~H1~`BleG%;i_u5zN4~Y! zHGK!nKs&~b??@u-7ch-vr_qzp;q@aB|5Orh#1>6|aTGNjeB+sFu z7OGr&S8Ej(4nv{{q&^ZBKL>l_2#$=d+H+I+S$143Jdd&!$&yd6(74m};n#2E7l^E-+;~H0k{N>dH{Tz8erMzwlg6t=DS@x~njC z=v*c3t&5wb^nGQlBdqt#&t-mjcT*NUcGUdMJ#=*vCvGNlUgREuUn#p1zXVgIMq2## z5B^s>3KqM2ct-ooRX2)1f%4lFZ7M<~w(?%6GYU!hUmKiABs)osP2sH0cY+9o+P*C4 zvQjcac~e(7&_2T_O{2v}lt*Ob`Zi?Aa1f$IARtQQw0=?0^-@fg_SGgCjTVbuOk1!9 zQKv$VFX$zEiVR*G#+v#?Sjc6S<5Jd4;#5&EIAM>^56NHXfl~3|>#t2u+nkn_-IEyK z@PsDoZt1aITw^ z|Kp8q#KKio-vn8z@kn~4gH}T;GY>|q(#V*wTPR$V#*}PjTyRNz8eiAd28N6E+$h9P zu_(*gORwa;!|(W-XD`{KfCJf$w~w=<}1F7S-c3BcI~?(6k*2TdlEI-_0Y!@ zOj-ZKr9g3OC}$`4;Y#FwZtE*cB>A=K@}mcHLZ^58Cb1Dgc;I{}Pwk;w>4LL{p9t^D zw;qB`3CuO_BSq*0Hyu?DwoL4TU;lx!FlW-fyC`Kf+&SB1Y933WLHydgc6sQ^pNs*U z<`Y=tE3H8({yQUmE0+CyEce9uioz;f)iQkhK4OO!HLEFmrMBqTIt8WZACI7BlSz#z z+lAz-0%_~bxe0mhnm!4#VP%_m1jOFhY;QIGw+#6gb#EoVM@LdqvZ703K#B!J(8dIj zOw=MVTQw*m9DX+4wp;w>@K0k>IEfvsN$__Ww zkRJo73(^I)hc+pa%k zM@1Go@0PKEy^+oGS||d+vFlTgV9EatC0`D>C_93_NID{dxHq-R&17_QHoSuN&$!=Z zAXzKgh30lYeDOo5NZe?!-AA_9Xfd>pw4Iy!=>3%MI1sEIZrYiXR{EPeBl0cBU8L_D zjbXmxqXEn9?)7hKrtXhfn#R@izFPEu7n&r+7c>xR^kGxa6*h8&qZgCS6E4Su2eY~i zBvb2rW#t$a_D9Zhp+p`sSXJ+alL=z517@}rL&SOr!0 zLn#oX`*bdT+m{ZM8)@TOc3M&=Il0&~{6%coX^ouI3Na{&hwjA}ZJv8_9d`L8%otOP znyDC3{-&o*ieUJLj}vKk)DdImzOr}8!K61o>H!v}_9B$0Q}uCe{;HTy$Zq6kU*My! z?(PF4V_p6Jk`e9|z7B1qT2ilZ-`8gaY#%1fpKN;H*&f;fEDYOQ>9s$$Mds}-851r; zhVn)uESZDJ2X0Jc+@#v!acwaoO&{Ms`A^jPlh5)Epb)iGYEv?yNUUhT-qh`t0KHKb zX4ji5@%Xu#PkaXH8F9HaBtFpX5VxxFz3<-2kUegM8g-H&`W^#|{vb4-57#T*sa}<= zHNnjvK_W0|x%uzjHktJH?yq`RaV)HH;Cjw-T30Xl4m8@uRdX7OLb{gp9~QVu%#CM; z<7=(Ah;wPB2vK(9!QWiwn;=D>c{%(~Hgns%MPOaw&)vKZt3Q*q-rILQ`4D|A0$EMA zJ_nvz23;B_bzN{qV5fB&kW?^*@4fLF>11N%_R5}u_R z%;JLKWJm0z|7v6AZ^9)3s&XT+a|jq_LQ=L%E7NIF^_vz> zkr(&Omwc7-9}*r$Yg(Cljq1o7bOgzxqAtv`5Z@_?0*`94skk8AWdF>b*I!SS3q^;W z3`kYmga zkgE=^fux_15~8(ZAVPE*b0)V=ZWraKLuu7}$1kGMCo2$GOw-K$eiVG<$Y=+%b9`VDCI5+vXv(#n?02&-q(w zMlFo$V}aN~L%6{v9?32o4JyQ74ZnEy*WTkhe=?U8DYZGi);j)0PA8hi7BKW(so*5? z^105KT)zwwKkz!Esu5E9n9iAXbgf#sPc+s#qvS=4=HCbdl2N9lfL1Lj1 z>oxe%+q83jJhbH@tV1LBuw{RWyE&PhW>?{@i}${ii}&`&c(Q#U3Xh_7%;jSGA=E!o z7Nqs|x1waJwUcU(3Oql?Rrua75}f^9xv!knX!d7qUa7Y>T#;apq@J^x6}WY7zM-&1 z4#~XbpDr(u(%&}R@@a7qCRP-VpOP=%J-_pxRQ-^qH&$2_g&0y-OaqTbYjPJtH_@W* zK4tHQn$d6=*xvu{$IJ@zxd(jb-QDvJ!F7@bs}*?HOc$~2{_KgPeu3jXqWZ+6ExaNc zW`j0jp&K*_NEpdPtrX82D^KP_ZwC%EVui~V*8K8sK=_H?9%#kQ-&e!JeXlx0X3_gG zU#GNscAYwV`(SPmPgm{g{G-`&t-*FB-0=FHZxXPgdQCj-=G0;Lox@cmhkxRHpF0<1 z{Jl*Ts&m?aoxQ&tP&e=%#3qUrK(l9RhG`LK>lPt#tjhF0$DgqHHZs)h+#16&Zc4%* z#mV^RbuKxAh42=4-_~2Imqy2ABBJKyi_Ianxh-U!BL2;)zG|s_%&^)&2XGb^grFZ< zeO)|mF#4K4jR=s>*%ZZ&49F8ibkbQ`0~B9@tnOsIV&p}OhQ^( ze2RbNK(dn3z|uNxkCki9!H>vfbG8r17Q@hU#=;_VJ>f3%6|v!)V@?!&K|;D;P!v5T zk#3lR0l_GEQcuF4(hs*Jx!^WM-PPhp4^(x=DbZtMk_<+ak)_0_;Y+1jtZ(nGM6Z%Q2X4eq{QCI{#=_?oy<|WF^RL4_519ufgNw|YsN8Vj zC!??W)I)rinvFHbUaAwBp}n$5Uz7hbz0mqk`|4bxF?+=!(LIq{?1cqENB>se9dU)y z<*EYX+f?)HvF9|Wn!(k(WwV!StSf1J+^201QQO`1JB(QheEW^F5#_&6uu2@St_SpU z>a9jzc0ez)oNk?XsTYO(jLQngQj+3{!9>#atkFLXjn1sxpjZ5{aqt{nS|=L-7Y;$9 zOBvV`CDr1N=eY#=Mc$MVdJX%%Sm!`9*6SBT(SZu-2zk7UnMld)dkzDFW%}g<;)gxD z8tjuxTxTy@r?_0F{Y4N{O}PDrTXZ~DT1@jtk$Ai#&=lfcF4#94`SsRFj)6&=FYU2~ zQros@lo`7Hi*mJi)?$h*|Bj-a(m3Ny00re@h41qKF2(`#88OtY29#N3B%B4Ry+2M~ zK07y3=&_qK=Ls~3f3oys+`L=2FK-o>J{H^xXO!?FvjVp>WTw3OdiKXC1SidQ4jIYu zG)=uI&JuFdDwjX-#&+j)9!9iX77IJ=8;)K}#2b*4r}W3hz%1rvwA4KnzH>hRP-YTq zHRY%yP&T_x){U4+^f(%MH*&yd*y6ifHu=~lxSeLG*``2=BQNYk+1%@P!i`5+2&ckv8k}souRP0-b-%Orc_x@87=X=uU*eOW6qtApr z@9EN5?bfHI*A;>@Bc4mUj=*m1K2FuFI~8p<$Mi<{Yj~<<>5<_^Jq1N?<5_lut@)8g zOICmU-JHE2r5hCXQay1*i^eh3u-Dz2Z5ft`lAq|ZpPu~aPvh#nPm|^k53-p+x7Rj) zq`L;kk~mgJuyjpS%Xq*Q9u{4z-O51Ld9i{V%CK(UuXF zh69$FKV`qx0Ut2JcGu_g+qx+d@8f~ea5-kYXv<}Mi5X@!Jv#!GX55VUR@Cq=HS^CP ziA(YE46X085TryfCKk(3+2VaQkLzCZbdlfX)xP^?dKw8@)`Kk<5Q3=njS^^J` z*ta$pU!24aVRL=-%RC~dILfbLuWkA$OKfxR9J8ZvBs9m!=zH=?=DN+e8>{vGRnf~C-+FNLpx9dA# z)MOE3ESnesarF zn26YrK))siIhJ1>sh6!dTQ1uRvgXdEHk4 z*nUML>DzkEd2Eu|b^Sx@m53{GK@b5B?L)5TQ{*S)td300o7|F9++75Ky#k6nu)lqn-n_N( zx2G5q1<@2a>X^0a%uI<+u=jQ>&Q!sKmOLY=krG#nnQY4Csp_|1ki)ydfqQ1(qV*C9 z#$?%#6x28jVv`3o`Sq9n#MVc>vHm^&(L0lydFimC158}M zOvW~ID_m`&&D@uwfP*CAvVLPX!ol!or-_31SY$KETi?9vOUsK(5DR$A4oP8T~3lv z0p%Bjn(a`&YAri_cXi}6--|mJS}#AH)(z2nF`f^P1BUA5@xNx-@AcobU6_^Fg5^@% z63TO1A^qh$;!!g-_TX_ZNO0yaa>fw$0Ii>F`k@H<5P=X5`Tb`MX8a&0as2h2K!d5- z`oVnnqMse5zbOa)$=+;ai~k?B5{Qz|9c|m2cBB)Uq<38F6-HZ~$!wm73yNgU!c;iD zB_e@GIexHQf3s#JsZR0M2bKdH_jyvh4Kn{Q|9bZz=x}7=!*NBlk#a5cS3y#em$b#b zVaU1KYKfRx_f215@XyVDZodPP%^V4}6ke*x!D!XyqyGL#)>Ys$Mw>CY5=L~^i%5g$ zdh^iY)2u8s^X0?`(P+m}|3JOPoA?ER0w^-58UtuSV9mbUkHtaS>-`hF#IGMqCT}D* zBdwJ2?zc1{iaHnY7rp7o?d%>G@fQ4E9w{t!$uwnZryN@JIG`{1EzZS|UiLoo=pKi9 z(YX-3k)pGKHwCqDlUm+E8>|;2uFn{#0*99E7R5mqF3wzrb60AmPUc0?9WI=)mz?%;7P=v}U9CvD9VaXTan%V0lDdBw$q%Yv@rd>x+3yC7g4w)9Q;# z#E*V|L+?)^S@;s94SeW9!5(Oh=disai7@+A-(OM*$K!gaqO}Kp0VReG^RlH#cCm0- z3+mMkqLQc3VY%th&;USE)a_(soMV@i!SN3ip2F%Ya8XVM6iOpReQT3-v2D!Eadr!b}!f}<>coHjKjf5M`<-5zFi z9$u+7l_hDLskfxOQi>nH!6zw4f+f2nhj);(i1mwHhY zhZq#_`ASiI2KSetOVOsO(Xo8I@&1}GPSj5d+1lAk`UV|ki^ebCjNf*()=uS$Gg^OA z*&OjRe@VNz?NcA_W~;ka(-=L`qT<_p(`K@P&UAf<^~Or8MnShA?4owHUCCi!kiza_ zy3&%$MynISR!1NgRdB7Vo$WT8!0tjyEd?CSU5b>w(t-mpO;Q(jBp3_%vY#)D-QykF zaPHLOW8LQsx4u)!+?E)VE#FPgaS=JXjl5m`ZD*TZ^6u?qyPdk8*K?Tsi2&dawJ=#h zP=jZf9B$gwX_PQ+0x9#|P}3Rb@BO7>< zxBMR#z+tE%GX3_48p=Vb9u_*Nz2f@Rn-SnXHWGr8>m~gIhLGJiSe`i1=(fX|6bgpo znSHj4P}y{c75!Q0m_b&z0|xtqip6{bg$}npQm4VTivNu3QP71y(~mtcLx(;{Fkj4K zsr6P$!Uuf0Y=5J7v);diN28(QgYtg;>a1MgMakdLjl@+>=QV|=q7EV-nHT07)$SPy z7Hoz7pCzBt%h>k$Jc6fLW5(yHJN*~n5Rg%sgIV0VstihyZ+R*%Y)TBI-p&Hlf|mPA z^7VdX8k^G;+SfWQ(R1SJBTRBW2sy`f{hglQuP?qwK;!n`lEFp9CgqI(5rKM zZNASnW-7PWoMq0laOU)Y%-gu>xkI0DbtQ?^cL{X3L8SIa%cTN|w`;eq-BiqU6$-il zg_gYC726uG6l!p_66@gNLEYuOhq`$8w^xx&^I+NCu%{qcdhIc63AG@ZFj4-|ARKRN z#BDB|5t+O4M-VFGMDsnDhuOw;fDAn%Ql!mkHsLgup9B}x_s^%_0|aAO@YI}7Id?sE)OS8l5?T}^eTnIB#ke7DB^qETa~*bRxV z)J4vdonBnF98FD&rx6NYtL7BI9*eBhTv*4KD!2`;ZtqZiz!cX`N^rf#2Y8DZ&Y_vV z=TWYp;JqWb{V&!+kuT+m|!e&pp2Ab$93b zy=b<(U9!}z>RM8{y&+}m6xo3<;mI=gG}}kybp&P3BCbP&c7? zJ1aORKsM;pmzA2=VhR@&U}xVMG3_q2=jQMEn8RAmTr^(7#WMzl)n7!}!8Wtfd;Me& z1djQbG!8(*&aI1kVfWCn@wO#r&yc3Wfn@3_DsQnF{de?f3% z5UFH)D|~~gzv4Bq8`InddrQxP=$g(P6`3U3>IXn$1*<+$lg8Ki2JwONU1~lDb!7f0 zjxHNx&+oS$ibrSGQtfIOR%7m7q;g`$n zal?Bfas_j^%LA7jwiLTWm(-am$&i|w99n1J9gu675vp8pHUif`K0ZK*@q(qgkz7@F zYpp;ro`<<^Du1X#NkFLN*qAVm`S^$^yZ21*$Zg8y+@Yak<)W#ShKtNO)5#b~^bf`M z%==X8zJZo@wW`$Us!0+vt3ya*GdQ_m!vHI&NYnwY_K{e%r|$ z1EbZBo%-Ha+M9qx+nPs5TAS0eWd102G`8%>Cn5a~#GQD}ck2x!=or=MFhZR^RmQo2 zF87zi85Og|uaMPk7H)cogrg!FL`6TWEFuR5G#_S$D85TsF%-Fn&~;p$&3ufTiKqz6 zHS~DZIq$-7<+)X*6J)2f7}~zl66&KIZK1h2uPfL2_ zq{YUnQ!Wt`Zd4cx>vu>_t5Cs z?Lw_{un!qIssz*J+c7_79{VmqP04ZnYYjOe3PR225b&UZW9II^erK|ar!%Szex{W} z1Xk*hTppqB#Cc%|Nnev_n_v416gz3@aKhv(8kxky9I|k9Zv;IdZgb9VUVz+dny&6GvUbw18g@#Ar<4JRY{iCd4?`#@@0^m^88DyR zGm>zg{44XLmW~r90Jiij8RNHg)~bw(ukb&+kmAh#WyH#)W&^-0GIdq^nPcsXUY!h{ z>--*}as4jeo_~lxJymbPS&gYYy*z-aNd-r=L** z@UVUm*_ykJ+zpzaByhtGITeJI;t%`Zyps%}_n$kt>zD&kYB|Ti1;5a8$JOI;_kf`r z)I*aNjU=|}Xl&r8C!@n&MM~OZ)`J($s9!swdAEM-y5U2dVTpeTTC6U^Wj$o@0ZFJ^ z1R)hv_5#DXlHQ1#bF_6JQrkMI)0Lgr2IIKX-)^Hq6xI!#$0Xn2FB&?|lp5znB0d$u z&+lBeHuVbyKiAbBNo?xSfeEDS&~V+34GMTd03DKb1ZAz4kndfvy_FItBMiF0%4zHH zVKednYWxVWC#%IP-F+GDWD-VJ{FHlt#Dnu-$8WxSqfd6!{ar85;HeI7o@#G3EV|`w zwo4w`)CLhCausPCJSZN`=si``yEZjNQ`c&>qW2J6Px2ms&9cLbs$nQetC0ftTI9dl zx%Nku@%$e~}p)WA1S)@liD^NQ*wE7nUhR&ThZ6bAe z*v4nrV@NhpFN39J3z0LM+82v&r|d_gBU4QgUP;`kGovr$BlcLUhrecdd1Cu`dwB-x zoh^EV8t$U@F|93luYPSi%#_&z*={qWNn9=3UuEUEB-Ev_CDg`WEjr~ITY5ZAjjsy#(>%!uT1y^W_ZQy}Ep}tjuBe{X$FmID z9kw^`0aN7-5_>iGYy8S~MwoA!x?g54-@jCf#V=`DgmeWq|ApMr zYL;>*{5Gj1d{`=;*;Vs#`~P4%q63nu-%dAZ-+i5{-E5ulC5*4RyFX_%$dy%h5Pl)j z1ktq<|87)gJXu(l;@6czby%TC+20paT)tYh{{>e&$ze6Q=0tz;FfC1_Z9RS>es$+c zzT@<+!Y+4pW+-P-`>WgPq`lxCr*iem1K%0BwtjqO$E0~;N@Yh8pE@(ZXAhds4XWZp{zDQ0STL?E?N(Al z+Q#osq2MP;hNitZ_!E#hWjNP@soli;V3d~Wa2{TSi>J01FOO)JOQs_-9DO-VG`>sJZR`>V-@VS;u##lPAdmN9}EY1BBm>1 z?WE%wELL?{!LCn$FVI!JfC1p^XaH{Q`9^*KB&nIlI^!Ngd3wMYzNc=_xpn&sU%$b1 zYeg&*xbnK2HJ0|*iaOH0aB5$c1N#}^l2HEJ`jKI#y_)a=o)Fk7HlWIW@OjfSDc0u+@xrfyH`UA` z(RSINl(x+3&eH_l_mkcpc)NB_vqI5a`{mc)_9QoJ9mKT$3@at2ja-1%665vT%5U3e zW{|bNw%3g3mks1|HiwSafpj^5x+lcDubyDCW2^x* zjPStnNc~z7Na))Bpdv0QrZ5?1!Lw=fge$7ui>%EY37{?@r@;Z>0b_s_qrBQ6sOdjI zPU%F`Q3t+xslXSWd_o>k(i!y^Exz^8%Ui#vl%-z}co_GoxHY*ISM!{V zGbY-P0_B3Vs(%2uZRhHKpj_y&uM%aJWWK6%9koPt)aZQJ+~Lj#YM3J#5@Ph1xxjJk zA{c`db1&H=CzprFu#%}g)2ZI5Nk`4w&p$CcH~QA>U7z9YqvQS^ zkaz_`zgLHCe(2({%T>-e7LkSM1&EG`drR%J5H#L&8A_4D$;fBzKX?-`Wp%QV3Du16 zZHOFCtASAI{~_h;G$u)w0l0SbmEuri%rZ8b6j^E!Z0+v2^06aG(6|l`32CH#Nk?2A zPs=hzqZWuM-*;yRyS-YDV%1=IOOfmtHAQOj{7Q>XgvP-(lNNxBpRofPOveEgT^7$E z$DbvEvhrREN+fy`!#hyhgMGYhk~5*WD!rc@DCKE$)No+ixkz^$E4co+XQQXy9jO}+ ze7setXZ?i7L2Nqjwrk~lYwK^$4x5xYzUfzGLj7Pw|KaiO+N5B4sa_vv8`PoIF=z4% z<@o=(lCp@5c&^=G6Mu^@5Y&ePZD>Vi`e@cKD zC?Atiz1WS!2BriSApOx*N@(G-S;&!>RTF*5d;*)4KCu)t%VYkDd;fr&4M5s6A7uP@ z#It6sWTD=42QNa^NwHdKcU&S$-@jkNhkN$2^O;8MOzkmur(*bouYLbF#if(2AlBoN zjE|&3e&54Vv}%oL9LjQ3XHsn^ipAbfDaweCTw9gmhc91b{)SpVm%VHXz~ zPbN=xv~lrbsIXZ9_3LZ0WgV&ftu-hF@E#J!!QvIPV61X~3yhEZujw45YXZ|RAp^5c z9yJ5L8=sMDK?MA?Pzq0w>%Rs0MvV)O!c@~AMR;6xwoaTYMoigCNuE3GhGo|u4(75& z7Lov^khD4L2cpulQR6RuIwMjBkXtI>0GJa%gT7m40?Y9DP?z`1B>%Lmv$9G{1vS$L z(0Z&Pgj@spcq_em{z|LuT-|H+IME%y?E&1os>m6XgF_2vJ!%Ba)m7VtRoOv>24p1) zNtl|dFis#k0EY^2;>Azs7PtwhbT-urQ{}10pwPqV=aLph%#<2W$jc`bxXgnM!P6Gm z9POm}_6b8y$~U?_wr=~NOjd>;&I2WilZ60`F_qQDUb!0rKn`d8zR6=BB{C%d&S?b@ zcOK}FIjI=>2afB3VT>w-SBQNOA$VgbBiZ_lCE4HVuHXdp%}m0#&ZOSYo?z95_J$k; zF@DKkuwN`ACHb$VPoX`^guic1s%|B0L&qZC`emCf2RJS$a4M1@-LJUt`-(V(PpJNB zZU6*?+7$x$Woe_K6y9R(4|<4o{va$((}D%A;42v3v>Xul+}0j# zVCW71`qp=VqBkhh+j_3nH!lO8kH6xN|MsHwr+Iyr&pT|X^q5iiM73yogaqEMpRXeP zINrxEKJiJW4yI!P$dHnfM;~O|=P44xtu9Y%NLt`v^7_#M!DSka=F3z}O&Eo~w~5Tm z^2d1qdj-6@gv3nL`swE?*cdptf%Ay6@L5Fgwsm?EC9exQt;qI2L*Dh854 z0s1ZVU6xr%KT+m3h#pkQ$_kZ1FsUwEGnuF3!c;CbP)-sx6N-OtuC2?ig1#02I3tpk z@rCR@jc*Ep%m5s|%9oSo8<{?Zm#{AUPfU~kT^Igzl$U_S63k^eXz0LXg$%4jJ~Dfr z-J`+(SU#=*Ty|M;BSQmyA87iU)}K@&zdsEn>CzF!>XjjTtR?pr_y1@*{QeIySY}8U znam5a-f&`~9x`)1j}PJlpUDe3;=~|El(Hs!OH+8DLidkBcbYM7byDj7-mtM(xe!Y# zMKxMVi|IwS_zvK&Qk!gDu(B~z{!oFQxB`DpQP3d(C!uj0dA1Fw)xFn?wjowSE!aj@|?L3w0}8aW6i zJFYogjf?kBLu^e>SYzfXn`EcKKhB{b?cRmO<5Vx8I#H+?tudj?N4eyqsTj>?#Hz*A zCzY1(NZQiql)dSCZ=vNeLj9)I`5)mI!?W^db}#x9mxql(5N4~NrgZ;pbeCI8L=*){4EeGMx-8^$R*cLMkas2Fy)Hund&FJ%A&4Cuypc zzJ+ANIN{un07V$k_^?`m4`ZCc)2|*Mqy8WHDc?!|Vo>YH8*89423Q1yqcnDK$=@Re zm2Gin-NFP~5?Oa#vSP96uMg45`IT~m5<+xt`20E=GZqz%Tv$OEic8!*xj z9QaR6RF`}N71N+k4AR)z--O1Gr%~8IF+U}=`H5{^gTPNxoq5(aZ*;9^s)ZkO^X(mj zvSpwKo-AS-mC8p)lotSb_*_BZtiR<3I`EHHyz{jp*I)1pm_7`0a&C?co_AuRHIo#y zF#D8zy*&$R1WdIg>DO`S0sLLpaA_@I5fTkcr*Cy!d`=L->T&R^Na~rQq&dQ|o&2`0 zM9!bls+n|*B=S_i*8Tu$wqxyv=ope1zTHq5P}~2)4?)Y7n0}qTftv$SKk7qdJsdGdb(UZ zxx9=diXp~JVj}gp;|z^BD4=Dq^h;H6-wznipvZx5{Y(-t6-&(m^Y8qW|B;{(^zVDH zo?aiAB=v}iklAAyFzq$y=tQK5$=^%e$jdRM{>X!^%w-Y6I)0*0{>?Rq`vpjq0dM(9 z{t`b_L~qEIzJ_DRNX+_NX!+cjk`luTNkss?`wI{Tn`G=d7FCB2l&`-RdCfvVqo?~}A?c%rA{j{5-6)PNMJ?I)^4^Kt z98Qd0j<$>h0g4)k*#D(x#Z-*4T*Y_se1QjWf~Nja;1eh~p7Y#aqMD1ff0!adCnJH) z9<)EW&sLnGpkTm#DHJWwhynaS*qT>3?D*#jlrJyWi~0|go5%l=Y7#kguKfyO+n7cpiHqbYSr4OmJ3s4KCoW?C)NMj3IqU* z{u802@x}#A%E-9B4hIe{X1N=^OK5F^vRZUpXdGA9B5f4qIiT<}cNz?k%m<;bPy{Oj zG{nT&9j#Q?m1@p?$`4VioUe4d3)#6-l8 z9o`Up?EzJ0ino(RGXYYKj1qKFdS3}7QM`nbWyE4?e9$Z0okdU#>S2o0>Hlj_K#7V7 zu(NslczK84gh5LR^eD4lNh(^(sCuLF1agFk$Oxb!Z_GnIBk7UYzQVYRV!_~}da;-z zJNXQtI04}@8O#--0w}rv-7TV2AGVkKG02K!p&WFjK;#T1e@#o8SB%~$f#{LfpKncK zW}MAnj}?+{ikX}rSmIG4+|~3$5|iSg9Jgh&rYNGjJoH~n3IKSXMrpP&2rGSi0bG<1 zk@*p`5%fQ#`$TKjhtceTrpZJ^N!gfUn^QoQPweazc;V2xiq$Yzgb-n#VhOvs25jb| z_V@qwrWkzHi0=;%P^~DS0$xfYr_4qWBNG!T0cv@0fn;-x^5~M1q0;2+v}kXG#UKtr z;wQt@elZ_vJmA#)O>pJn#sWCnPm!Dj?v;Ry&HSP zq~2;bnUs8}WD@z_m&sii7%nWzyvY;1P#%(-J8zq;>*qF}(0?69)PJC3#DU`8FwK!) z6e7(>1#+lJIQ4fscfbv4{W7!PLOn4=jE1=V+llqQ8TM--#2+?8iCPRjZw3#CvJ!&~ zu`>sDc>xK4jsYCx?`*vMKMU$pIT0bYbMl=rE41*L0{wkBNRO`_P(S|AmgzF7mv2ch z_$2dn0TNINsjC66iSjH={>53+U&;?}cBar?f5_Ia2T3Jg(*IaoPn|J3n2J43&hM^K zrw-=SA%Vu=ZObd#kgCS_q z_J>z;!>e+f!oYB6qCRgB+TzNUNizLr!Nky#jZcJ@oVZYUlVsX#0szv+3(=fLBR|yz zh=EBgrv`*V{w_g8O(A}?{G8l^2E{Ox=;<)=3IekVnpL~;7h<9j-$Rs>5orrBo;S}U zBA`jDu-lb;d1c3K8kGn1Sq=0J=R2`$RN&UgM4>-t)F9|4n07;VFLS)k@^t>7Je^>xe@t8sj_YqA6^i?-42)EV&_pA-~ z$cP4}6vo@myq);(Fu`%U3GfV1RS15#gszENg-cuhM;kRRq+#wtb37OrCR}6}!ZB(f za;D7!=$M2^FcFamVx}#!kc1#4ED$nD&iBBnI@AN+`K(PX_$feKE04x|XG$BPSQ2b7 zCHDW=dke29zb|goLInYnln$jqQo1{(Q$RWfB!(VP5fG$PTACRIq$Nj1I)+XGk=Wg~;HTA^2dE}Ty+!NWc-JSd^P@8YZdLC0 zUV8QYyA1ikR3H(5wi2(&Cop7}F3G*6y!(&@;iV)Uj?WCb^Q`^az4{m4Pc6Kk{)&w_ zRf;E6PNwVP1xms}hTB}z7M8w4NAP0s>^;K|Rmu0cmokfkx7~j$O$+HQ!qixN$?hwy zcXzVN1?G^PFYpe#lkjXSmQ8*?JpauVzT8}Jz$LwcIHE1Igj!q6O6DAHAbDQP$r-o5 z)m{QhL(65m-1?&C>LvF|k2|EKx&Jgo`}uQ;7cZRfkt32TPoPK;bu-I=|EcE%d9HS5&eePFg<$F}xgF~upY-;8A-zUe$p9v@I z&HadoJn)p0cksWWCdCw=YxUw|BfnX>f(+Ba&z1pK;okFYh~c+_Ag#wyCUKR%qG0i? zW0<_0T)r^dZi`U#WGW10`z~CeAj(+6Ir5smOO9*}EI(%iceCzF(vA$8__wPEnNl3!D zRk*rh>3ep*Xf+|vS3|8P4n@wnx);@LhNssd-^*;Y|3e+Q=`**DgDNNpB5R2g$W zj_=hM1&-8xuyOUbu>iY=7+4!?O)DG?N{f!D5NN~q>mk#dS$)l@R&0AV9Vz!$u88M^bNj}lJJ2FQ<_z?Gr9WWb>tw{#Y- z!fS1MLQB^ZtD2#s_vH7`Cd19|1!u@a9ySe{CzcQHM7(#t`l`=XM}X87KT=LRoUHW) zw3zaiuclg+!rvXRst%;*&oVyNC$`Wd?%A5G$!XJZ$P>PMA>-A!;J!Rz>wT@Q!4UM+ zdD=54Rt}wcu6yTsvdz=Y*H3nt>Fz1#vLvP@S#v#sMgsLZPsNBN(=po`WBQ1x?5MY2 zm6^gn8<(a8aaCVyZ;<`>A%>yC_G(d*9$-#v&ty084b0CA|V+9elnR;70gl;k`q zOs|1_$hkd zsPmj%V$U9E*>g1oG=1&;|7BHKW+J#nz&)-=@CsjfN|T8MNs!C(-Sm$+1Dplh@&9Fn zM(8UiP2A}PPhk0%I?v4$)5oVJkIxMqlZfBC)bg1J5)_k ze>ODoO#j#LcL6BpnpS;x#?SFRQ)Qi&_DP^gZ(c5$OG{aX8!HPRC!g7&TNW0I`)gy& zb!v+ny&padUaNglm^v$(BP&A**HqH%OuvGU&w87Qp8ig}82z0gx`WvgheFJ(8}~W{ z+lvjGDznv}N@9i#qOtfDP%p0|d#2Kw@!1uLJc-8_=acdrRTtRYiR*t?bfqiZGBGRu zjBqGHT)C1%hkA>;nYO6t^}l0G8R@f>Skr3R5Y5eI`pDkgpwMR#DyMv_=A^c1g;X8S z`0s5|=ds~mWRA=~*rXqt@hS>>`D3V-!Gb*04kI>QJ2E>vi*>W(&#~^Nz;#_d&-$t| zMNK&3$BCDdldEeA#Dt=Q{CZ#QdxbNB&}19dBo$WD4b`jZ)438DaI_mp6JqM6u;w(Y z!4DaU2^A*k-T9L7-ONgRCRX;-$x~BPev;8Y?nSgud?UKZPMEF45jXe{URv6x6P3@q z7L)IeAAeF5AZ`V>*3P8PY_^Ij&2%^OPPDsmk-s}`qrp+FL}7h(Rf{dDJ547sxRNN| zuYG!IYVoNVE>1hTgp|@1oS_J6ZaFYSleFMT^66=G-cjw*Wxfb^EmG-t2g(+r66fjU zMMOfo{Hk)n8)DQi7NAfnIP@Enyn>HcGc7x2dp72Td5d)RhI9j!?*r z=)ksoA>zz@5%TW7u^QzdN+rP`s6j`P)lpv_YZ}vJ(VMagE@xP<&$1kPId}! zZeF|_EBDx>4591aQ1QX5_Zv8tW8N7$+NN{=~z`e?Z`u~ynbamuBW4; zR`vCcGm@f)u@#5Ti29^8WOsgou=;-Q{V(Dpf>*^CV*|AH_ z2`YM%c*^mBF}~3x4vTuB>hb9m8}Eo-x?X%iYy13f^`09q20s`ZH%u)~ z)QYcaI0xI@FF7+;9VEd8>J&%SZ2tANm9~x(lU19x7>$94 zN7cv-#{RQPUKtUx+tnz&TR0_8%QNK-*Ve0**4CuWH;u01(1~6suE=?k#|vk?X8`jEGxfG! zA-H&(pa)u@u1>xF?r7;sxSR~pAyG5^kOeGm#3V{CRyW=q_Z{!b<oZ&FP zUrN0^Bg~23Dm-yasG2=aE$Wx^#khU7S7p+f8W+#v7O0UQ*cgzQy4P58PCM} zfN%*KU~qN2tn@EK=3RjiA(o_dcDIelN7XGkW^Tz17j4wizSmWDBoJq4E}5^(TM&8@ zIx8rYY_l}8!SbM=+n+_R9a|z89X^z1&tGWGo7x%&i|@*hpRtUyB&QT|$yj-qSICy% zfY6#uDf{=mZlGGK{%VlSipX6<{=((u<=G^~T1Z#zehCM`HRlkSYsxllP&25fmx^y- zEzsaDYi)7%Ndy9+t7DSj7HLCCX`qwS{nFRl_QI}yd;o;s(IJLy$vX53Ff+}#I3n3f zNTYX%c0VB;QR&|M;6r38o$?Hc!k|4z7$aYOS&1A2eaD*eW5{S!$$nxz-qqwsBl;{B zE63{pEOn?EL#nnF5Nkz2Wfi7-jt(wsX7$!-3eQE6MYqCQD?7A^c?#b!DMz&Z29ISM zTp8keSCe#&jNt2Oj;Xe`CGZF>shUxGyp92(e?#2HkfjE%5ieBY*gr%rJau$((#hu= z616q5oyEoaUAuBQM5bP7k?V#P7tomg{&!RzUGqxir>Zk*!@BB_C~)bvSVw4Zts_E) z;6@M9>7QW@A&p`loubg;*xrhxT8k_OjTgF2zNcO5Qs{uF97@MaY>E|I5(NF=BUa`UV2_I*g6cgk@Jne_*5)DC)r0DdZJiM+fBRUf^;P?+ zJv;el`Lc@%@Kwr&%C4ODppSNi5BP<5qj0mrPz?Z$h>nG3t5ABQWN{Alir{ar-h8VV zC1aiWKn!$%GV0LFVU7xPPLhi673Q8w-Dl5oBDy1_$IU>0%goGcf30n|4hM&{f)u^L z7N^FVtON~Hyoz+uWY&q0V*o!C>DEv@rDTpC%49w1S_nJU5jQyndwT$2F$V2*yR6y5 z7(Eiyj;^VsrU>34EY8i9nSc4Th!>o>{NOVu-g&RGTBP$5$%fKXgbw1et+l%^+QV`= zdZQa^R$u0K=J~U1fF;#4QAt>ZNyk0oTOL7&+k+?tl5jcR6Ix}n@>EVxHbFj3G6c>* zMN_+<#P%lNB{S|FRd%6idY)65kn6|%ZO$vf-#)(i_VLQMueRl=iy}T|0O^nu8=NV8 zR3`U*8qv-}I9V&Om^o3oW(<*g$Mhy&0`$zi@FSP2Pcr}xV_WKU<4tKUxOMFcfoFLY z;lyR5)(>gmK;}!Av>dg&67O}j{9;MnF`u<1k9RPxcFaLq4s^KnEft#5@ zoW17+d%{owm7)e4bB9}HsERlMG&UwcV>i$Fky45mo1gD zI>pVv$NK2H5@X1nE+^-0y_673a>Pvo7RvW*0+HegLq>(HC8G3Dsv@-HxZYXvzPO~^ zlQsowuJhVmN_@Kw?w6V$?9`<>>~I$hx~7aH57oU(%uf2AHLk$fvju%4L&e8#c%C6T;U5@w+2l70F>`+N0+_Z9CcFWzO2DsYzl zKH9B=(ylWUE7RrGTLt_XNUy|F#JT#DSk! z)z|;bV2GX4Hz}#tQc|Io5BvmeUeh^zAPms>HRmh&mI|@_ zy$x3ifX0>)6!8Z5O6(718ukhN&$ZW1RCpLcOH2`mH2h|r1XE8E+Xfas{$&DKPF-l`2KDT&o2iz0E z=Tr(7pK9b3{p;32UiGRvt=394KUxxSmiKG&(80_sucR3$o+^C%jOxurpmm&YHf4wgMjpN`#Pl0+--i*Jf^QGAPZkz9lSdwBekGkyTY3;APHq(VyfJ?Kg;u5uuU~Y|7L!?~1 z%lvh%lt&w#9ldcrH~znK6xCU5CyyFMGLsQ3NrgCP$euxo(jk<7n5Xe-{^si#NGieg zPi1G$CG7mX_<&CZX{q&K?TlS{-ojRF{;Ao#wpg5Y6BcFN-SxDvto>4jV+xb7Ts<*mZSrw%}+a{xXt{<`?I>_vFu@9=z) zT>OHeH_X6V8z!q6nZ1rr$W?K8OO7s|>P(_m2(|32=9p_y47DugrP9)S8oS$i@u5q5 z_uqTtsbmhmb+woK$j__97uqFt8K9051VeFz*4Oek7oh3VBE0B@Lx8$roS`;#+?U*4 zYxGvALpQH_eHr6p`H%djH{%7<>3jCfzw46_BWgvMre7EcUKF4pgIdq-eks+Jm^XG* zERq{uJ*{q3%R24`3=q>@76|d1{634cLbV=$eEispH^^bNHjXl81&-iI2d>7hHGoK{uhjh{oVB)5<2vsv7y`-iz%#9wQxC*>86X|&yg&rYlLBwIFs z_mUflE=y}?sSU?F7*SXyI~11{%IM8YeOt!6D2f-)Sx4uQZV6+WmWPR4^e_n(kii~e ztxW!F*OYxfb4(B4f*11UH>}4NiTpD}55f>HrAX*afJa*6-R|GfsQ*w{r6B(5EG_J2 zy!)@ZL@-wBh!R~M3nCx$i!!u)3N7se)I$*8rAO;L0X#*>@v7-0-?f07MB6X(-E8Hz znS3322hQ+dpgMf?Ex08Bq{I&vUx&b-h?|F_V+S12{pmRB)Gsnuc{o**lTM?Uqsrj} z`EE(PM&1AHw=w`%3GI4dghGbQXHAl~*4{^|aX@-n>zlvgu{X_AR!Erlh0L^VQzH{(y% z*26};jo4&rAH|g0{x`(kiI(JPVl6>)`DIyMopP~>g+V%70Cqo$D!E7TZ(3aHYoeOzrZ?${1*4LpQ4Tv}W|4Bj-XwUPl;0ts;8V2x~LhQ(6?6wZUV4$Pl`g;VJegQ?;r5v8%`Jw zVQ%a)m-P&;-x9e1WtX&!9ob}o*41T>D0lsP@0=-@48b61*m!)*}?m z(nAMl$8BIo))5Y${}G!C8W6o=3|fH#qr%oh$0~k&3lD;Kbt(PY#b~_xehY7__qno- zaguJJbi8G!(lne?{O&&yB4@2}V9)KYVS{hJVdCSIBbZ2adT>$bB?T4Wti_Jy2{#WE zm?qBHS!+lAdxs#ukaq&>J;uMm&j^2?8`A zkx#P?RpPV7M`-zx9lVVF4|+Y&V2Pz+dKRavJCxA;3X(iZ_OI|nnqhux6PT}jy)JW- z{F=szaqGd4i^-xpDqW2=NvTu4V6l*@UIUXo6XdNO{Zkh)Ff4cX35RN-O*aoGMKWva z`e_x`ik&>x(kLX$)`$QP!p!IfZ)EF&-V)F2Ga!Yl`r&EE+Ca|h)~&`yW59M#uaXa; zEB1VK?cni_Q3t92>K!TXD;*dlr2O%33Nrr@VynKZSQv$0afOWsibsqX4z zQuV7DgEgCCv8kOz`Pjt%LaOryN`<@M!*4U0PTY*_hIW7Syv*FC{D{OAqSI8P9_qlR zr3aUS)TlLXGQ7Q;Lc;`CI}uL#>n(j!DcHl?v$SzO3KoRQ@>XV-wV8}p;g={(JDxR_ zn(ZDDPm);TIXPK}ya~zD2gF(zFHr$i9o9sQWzrg8e`y<8*Ce#N$Q@9>OHQj;;9H@k8U}Q`eNitCv(> zZ?KIGA>^F=y9^#%Y$|&xjNFZ4H8jYxfT-sb=^Qxs=xXXZcWS}UexwA%CeD_07Y?4! z<*yi4nC?Z439*r&DuYS2u7GNPg3EXHzx$r>Xh7285`{ds%|LV!VLTbz1&~h+l`{zw z8HX^*$xJG>{a!b%dlnwuFQZvtkJtTawcrzjY!62{_c^!xYVTJemHb0Whr+1JMG*~y zBr_(&z>i&u^cT{+e3cPHNI@GCrl=+Ks69ua`eD?R?p$%3>K?Z0m6ZDIM}@(Gd7gzO?5z7wQeq~nA8Q3@9MQ^rtRf6!wcPn(2WI>fNwOB()gpLLR#6n`3^?UxkY z*bisIEv%;>d~MaVj@)Xn&S^Pa(p=)Nd(pA)5;)f=ATT=R6vHgC`L$WV?fW09?YNoN z81Vx3Qz&c}Bf9p5(R1_e4VN)149mfx8U}TSRiDKiuNz6~n3(uK$L#tCUQ0x;O70`3KW9T3R)8 zo$M#2J+*A=KRHNE2iClbbZgPF!&5X~S~6sAq@x?b|;6 zK;-=_j_>;|AEz8Py9P*uLD|YIYucxdh`v0w-%wWKc z2c(s2^M1>$-wG^5mOgX0>wXP}rjgKrt9#c2fN;umx`iFMCGiP9$(5eCFasI3$tk z)1sb0_tuALC1){BPUEg4&9^0%DJ0iF<2=#~o-Fv*fp0bTah5hLWQ9RN=Q%av?L)^v zRbM{!zat!*BXRa9Y(B)U!W7j<64kyJ`03~<`fmE|*jtam5Vrf(!*$P zT5{Y<`r=aYPXdc>HX4pP_X=l9%JhR7kA4)Z+}Oc8rbjDyO}B3!1(M3osDM%9`}FAURd=`IW?=BJ(ik{@x}FdvnF+LKOFz* zV>cV~%zn^NEvV%$8?4%~uUNvxjtQZ9dcti6!)@iAd(f6LdN<#$vdjmFG( zkFIZyH`1s4D%BDk6mb!;03oA$}mNmX)wS$3$NGb9fZn0VGrA3G3&aa z+)iGiyldN=!B>eq|s!GKiTEx?mm%m%ooS~>^b?BH`-E%ccpeE z#I>R4342|2Hd$>JN;@XsXcr=x^emSMf)MGf)Z>gqk&#$*%Nf*P7f~0;e3{@!g zSq`C!{u!`*6KqPuyOcM7XTHTJ@x0lq+4zPLY31)VGI6U$ zbfx{oGXK5tA|Tl*&yQ$Pf8LZXJ9Onzi!G#_ZHS;IZgC@X>gI5CR=vFHu=VPNP~Y0T zn3b9N{qH@lt1V%)v~eCWi7#5Rq=Y@Fi~NNj(%);Z)fFqVhgkuN@8wdmN#f~H?UUu& zMw)|CmMNES0kFUMd3+}!{QQC&q0`%gdmUN*PX*(Y))5;nqje}TE8XRc?Qe?N2a$ar z1m(@KLy!Ga3ahJY*G^2f$AV+5Y0{OLpcq`t`UKk&fe54YQ+#)K!9#Ji*udear^F*>2`u|+%)Xfta*hx^X?4xmsP`Bt-rVphU{!k z61s~gso!>qd+em~bh*)UC(xgG>d(aKimI_DH9+WUaM1w~d6x-#<`h*`hzMsMVk4yfMTK)#b} zgHHMhZqI)l7PA_Q!%!YH;kGJ$ie^_HOA7t;izynK;8oxHf_u|MTcSu`R%DYVvivUi zx>#YM?ar)fVBpRcH*4yW{BfxIVT)df=+@`HSejcCRAmcnm2p{0QZd#{2t$weGQy{` zX3^brqTN!lx-tNKu8?^uPdUy~Wx~b`#JZM$;4d5$pfV8hqRu7nu>uvaqY#4{7o386 zwxG?Al}s0yF*2S;^7wR|m(p)+bZm7$Tg0f!R^!}~vMx*jy)+1;ZPG+*c-BH3N;vCw zV4p(UZBY|VoVLqU@BU&!Ma8TDbB=tvVuIMhy!e8slfcmzQGC`_Fld}e_Cbv}&mZ#Ahn4p%SVfKZ&@t~PQ7^+lZVEt9nw26=zR3wE>KKVUGYEQmMwMC!Z`?N9hVOkUD;uf^ZX)L*5knJmJRmp@l(@Bw z8qI3t8au;wmDhOT+Glr(9&Ri(xXhv-MVFN95{<-nC2}=y&b81F3D1e@fb4x6ZbKx$ z{qzCCSX>~c-OIR92~Bp|ZJ$Q3b(gptM53Es`omUA^3Dn=?m+By*#sL#Go&is*Hw2B zT(>dMf8fVPew`Uv4dL+JO%f7A-_r-M%75nAJ z3%HW+?rt-5&GBW;@B=?aPNVAJl@r*Pp`fn5ms4Kfl25ufl&2 zD-p9D-WIVklfy^Im`7RCA(H*lg~ix-+Vcn%R8*85By9DQS3MIOT<#=|wBV<0^AT74 zeznJ7l?-{DV3!asp3F7ojXbLFo27ryX#m`mu70JQqRS5o>|uEsKDh(KUwJ+S;D#+f&@<2@_M5Ycd8!p7c3NFK>6hhY)Raexu77tWu#`=C`g{R$)D^| z6#35H6;#(DGv8VG#OmHAq!6{p7O!l1n6~}H>h|znN4CC|jw*Tr%>TMT(UtFe z%tI`3_rZROF5C1V#|R#Xq8 zv)E{&4b<@fs{3i5Y~zp$tjSzQpVdp>{&Vo)ryZ)`yyZ#qw+RwSAIE8lZLM6FdNd9t znH>4p?Vw|Acnwz&Ne6%X=TM;=%BpMziQ;PflfuYT@|WX-SLJ+n_41u_T{z-=-1}Y#u^qZ0w)n{RPv($ZPD@nnRXE z#;B5kSy1D;V-->qwn&e4`u=C$=pK@;VO(scbjT?3;Y&${ZDUp zEO0D<1i9WJD%2Y18=~kkc5oyQUG7b*n7kab(Qq<}7c?S@hOPQzUvj2g-y?z8$8R)* zWL?)9$*|V0D6|cEtj=ls!4mt>sR9T35zd0c20Xd$6-C<=&uND$Z8&Y z)@UAk16#Q*Dx=J%aqAN-c}rw{ROgUrwmXz_dGU062bkTpNiyD`%dU?7XVi!?L_P^c zl-WbdusTYI&l0L8ebX-tYlFPa48~^yQtXbu*&nT?3C~~+)qR^he84xs+sl1x?bF+RhEpJ&mF()JKUnkU=!lLxFIXv9V73-+C4rp-ZN z&ePq78ZXb$`&Sqi0j%C+qs}z(G^BKV*5~ocaa8_NjAY*7!-u@>FLpm<1+76L6NYHv zz_tE7O#d!__Q#x{geCWC>`)tpv^GEKus`jm-if7(GZ)DEwu7FM&`e;E0=Q~N&&0oo zCTLF>wVz{?Hsx3HI%U&Y*t_;<0$P!EaBS+|?{?DFLD=*2MkSb+aoa;kC!kK1YC1)k zp4HJ}*2ug5Vp-ld#%5=8HeC9Dc7BIaL%J(-zI_r~PV9@97qu_(^*GqR!Cpn<(Wmb1 zI7?a;+O%PqQ?_D%{M$}*)R*klT z&WXqn+)01M^8A`I^C#W$Q&&*E(>?6IXKN{D{o4||823$_*yn(d=Rx~sxV?_KXZ8=` zosgB*vi0V!!v~^XJKA=`{Lt=Q#dU52(;3k*Fw@T~W{;e^{IXV6G$y;I?J%$BUz!Wm z(zN)chXATrnf^73%9!L6YjlocLYC+7T~`_KHg8ZG|M{Wbe^>|AJo1olI{6LheB%8iZaPuipZV^$|jLm&P6odmM|_ooAn8ka2x8p@q>IiH>%IQN|)lw zPD(^u;^u0iIAKrylM|rgZ$TtIOojsaxBPiy%SVqXDu9oQvI8Q)i-UeExR`@;{v&n8 ze5~-K{S(Hh&N6}PcmwGPJVMFe`wu0{`iIG^`Wg;f&;uvO_M^rq!!lUt(X17j8FU6h zK?E-c2S#rpQ8W$P$&KP2*&7uL`L|1*cH}Wt!vut}(iAJO5<^ zdqHzwq!yBH@xSC|Zj@@axy}dOlLKT^%CohjMLzDsmM?xwP}kPA0BE57192q;D&vy&>cY_yf9<1csps35#n3J-!y002E3bZxcfjaXb0}0G!e#ST z{JrperV~!)(RT5%J8+o*BP`a68`U8Mj8f zGy#wOWTSv35882`Y_o6$?WYfh@FcOb#pu&2TikUpK+@XsHX$w88dTKRPYN9_jT}QW z7)&S*wIc8sz09n#f-se>HqFky_(+0D^9$n;%DB@!CGip!WJDkwv%m3zy=X zHWibI!_123U3o-B;P8#EXD{?Vs`0o=O@~%=-GbWV&+AtA78<~`WbR@-`bBik&_B)X zsP@MiX$e`C*hUTs!(@zt6`B2(+!H&ijGc znMh)cp=t^Lbwv8>xL-^O5p~`yN|KH7vcQM1RZIU3FZMZW)iV}TB6-yi6n8T3OXfI@ zw_nqfOI`gk45GU>jLH3O*X!z?f2v>J7w~#}T&ERk2KnSV(s1~&#%t#K`L9J9qFf7Y z(|_EcB8#t@Jv%S^gG%HD`kgXUh&yXX=OvYlJ8owf1@}96{K4g7XK%23oZ6f=-)@F+ z?@cb*A)CF2N5|)qpCN*JGH?4tiyppZ^pIGwPZhDzR*+h(TXb2Ar@mDgaxf#&_p#1| zCun5l+091oKEimWwY8P!gY!4g3l1&fz)@AFvba$_Sb6xjp5Xe`W?C$ad#irB-!}y8 zo%+KuNl|)7IX+sFD>fKuoKC^vWzTRP5wMUVY$pvl;jx~My z`_5^XMidZ_$kK z>wsrb*PL5?RMW5MCGoD>8E6AD(b>}Db?B@Io0KdY4MG0-_OV&brE!H`K(J;NFj;5_ zjo(lQ@%XNy9oLNtwc)C_+ciRS)H^e)%gOoz{I=V~d^US>C^xdd^Smh1k#m`^oJskU z<-M1WFLKlwe1Ix(?It&`vmAbh{5T0)V=(g_jiRu+&0(H{JDTFmldj12rwQn-(zOOJ zq^*0XS5~O$Jf`-3yW*i)X90X7v@O~Dg%}Y-EXQ;WW?`(>3$`S>VoS9@5wYSqg|8Up z$UL&)+Ojb{;Z?Ln7C8mvb%V1`i_XGM_qN1w@2x4nQ_`$J;p6QwLG6}f$?X0=G``zy z28WHRXIXB&L9SoSdc3CV^ZnZO^z>JUi>QkvQ@AohDRQz5ZRbdU#^H*WA06ElVq0JokN^YIyCXdyA z!uVsGZ^K%%&y^P~OjV7}x+*w_`=hM50_&G4E`^U>#%|72AWxJ16(GcD*k|pO(=ExY z-2zc`@vlp`obGS{jcwePQGXjjQ-fn9)`Yz<1@9HyZ0o0OcB8XY;O&K^=X4fcx=sZk z6rXx}DR^RMZm%_&nhIjKE< zNN4Si&jlEeo$eBp8l}`XeL6?o4a=msqxc?ij8XMMZ5o&?>`dc{#b3IjCWz{0^N^BFU-LZJ7;_y;t9f`G$DO1?bo@j^>_`hW0qApqU++_M3;{tWVOnoYTV`77J$I&r zLLQ`@?yLl@K1QC%wrgk7WQ!eI31?3$7q~ZQ1QIn@IXFAg90au1&m3h*5LKs0CLiar zQxOoshagO4G1)QMo{4sw>)_JMPaes9S>Er17aAIl3PlgcM9~<*+H>a^x=)QM0sJ3u zv7+i`hlP=Un^#)id;j$6j4{ywe73>d2&i0Ag~L}@?t0U6ZW}DlHt`QT^;;6~TN@SP z+a|j?(K?lazZw_Oem#JZOlmvBS_N*{G%zehV>qS%;=F<4hnw$>>nwHJ{{T0p4B!+q zFy{*h=z)DM2b^eqOeOsFb18I7j|^@DIZ7egQH;e~0oRra${S0-T;ufo~3u) zf4uq;m+V`6upKD}U7I`2Lz=qPJW@~l7~TI43{U{7+2?^jm2;g%3p(H-Wx6L|W@2s9 zD22*2$~KA%3i`Z7-IPyGQX~r2>0O+BS6}-BW)XeF&qv!&Z6D}6pY0*Cox6`3TMWM+ zdO!}SI*0O^l-1pBng>(8)Gr z?^*^ROk-VZEE@1k`iAT0&W!u_sXD(?UpH^B+lc?0=8Pc^-&nAVDR_T~c5>;vd0hz5 z&&MgxhXFb!JZ@S7Ov7(2Gd=kIXmK^*7#Xzx0pW;(4VM1w=;rv4;gl<=ITsu!+l+98M4HA}Sat4$s8NzYZ`l-ZjdpsL=jM zb%!a5e3<_t3L<$t_Z}9s7SsO1=qe5LOxth!c1sz>aG|wid!^@*H!_zt(>n*vZHv2F z=vgB$dNM^OnRaGoQ#C7bPXfB$aWL3D+R_Zw z1`Ce%?kI~a?_3q{3E#U*c>S<~@%GqZ-N6=*fd8^51i7UzD`A(D5%BXafE2I8vIoLD zs6^Hq*Ql!Jcr#x{)~O(V;sr}X&d0LfRNTwlm5Mz0Ip1G!5pG)Pg(24+xBG?R-sNJ0{y#|jM&q~ zFsV<}=dkT$*PZ`T6NenMq63UTz~og~{aSyYjHX&^M?<%;I+F3Maro6;dTk1Bz#8yQAn4y!;U%cXw* za%Nz6MILWhcf@B2*pwSZ-`9>bac`3h0i{Z{DyFm;bk&Ns85Flg!QDAtHdk!$++P(i z!|Kv2`jI}foW!+|wZ9%nFfIACeLVBJai#VgjaPC>qZEbBP_jpJs)d&85kcmXs}I>#n%SlFX~8&NP;252a(x? ztcT5k1@$lQ;R}c7_g=ay&-$xGHy2S-a5zyy02N*ry)9 zpTG_E9I6OA57O+N+VH@pcls>T3MKqc@Tcr1wP$?M?&8Or?~P)Jt~pct{hB@hmVo8o z9j0|)a22ToU%52zUsliLRetYH8IY=MaV_iB{>+OR6l(`<++8N+A@SQ{2%zD;?{Jmo zMt8@W>Z@w@HKsQJ;E(F8QMR+I*9bJ}Utu8M38cl)_N+Knd9Gx7wjo`2njzS&+GI$~ z=Dk?w@yE1<4(o{v#Fzk@x}V2V8q)D|yead3EiAS`Rc8UnJ~^J88|w4^Sq_+}2~1R4 zrq_gmT}I&V&qoDpMH{{Yf&PwI$nu6}ig^J>(sZf58AL&7Mfd2aeLDlMUUB&JzqJ5( zskH5(*7=@@I3|a_t1-kr{?$3kQRU4A__CW_q)yIbjCQA553>Te%CRs{Ebe6W2wSs+ zujq)`4`M30CL{&V;?seepmcgfX{{s68@zo3r9l_?x3AY`t>DLXoAFZW@o1T?I?&xX z-@=sanetS00P~j5-*FfBozEDMz}3L+3JUPUoAY#%=3~!Q6|VCLUM7gC>~=OC9|h#; z5D3o6=RIlrjjx`@qcL2lHoN&K8894v=wLlZ7l*hFujV_cU9nI#X`JST)VMTg)1lcLx7E^7-r!n4TfhDYvY)CWRtHK>y=UY_mXx3DJ!^ow zRqofma(gIjm@1SRbKh%9A6PIFNaxQci$h$@@&izH^j(yp&u<^~pAOijX;3Q9IczX1gGrq~Ri; zb_47+%E=~=L-m%0eN7Q#f84fR)*V)}zNSf$Ra&wV7bjQMXnWa&*~abK3~)h&Ep=0j z9mn7x{DiDoPkT*J99xMVYE8Z2ixSdLG&U5~zsq#KL`{nLr|w!)r&(h_2>MSX4l_ck zNV6$^8uvKK-ih|0Dg21|+#Y1@O6e7N9q-n8AoB5}OGcj~&r;7o=;6fxmMopMk$acg zeE-*1)msL0Xk^~(h`2XJvTstZHi1(I@4pQeozkq$oo2q{vRM^fs${HUmQAjBQP=JN zW^kFz6@<*4=}|`#K=!8;?XQ5E?mRVEuHwwZ;Wtup5X+EWuNf( z#{H@8Mw;Hh$K#`l-hNl>C&RYTMpHt`iQ0yu$%Bj+Yi8ZG*rc_=nlWI~`uQYQcCIoh zCwQCXgI3rXjjm&94~gW41e#By&)d8|XXA`Sn$od9p!^)81^)o;Y{~jnd>EYM2s>jO zb*T?o`O`i=cLdRw*ocMg2X0y`7EzkRJO&o7jOrm+_ozixm+YKBF)E!ew*zCRQuCvs zBWsTuJ*hog4e66`4r&fs$fT$%Z@|(ElVynia zlZB$!$L8`gy>;6b!x?#z+RxZ5w+t2kDN6859oG6XlT`;oHjADA@#)<*3#86#{-@m(I^v`Jfju+rxl_j;dZ5du49dF5&7o-b zKJlZ^Eiq`{36D9TlVKxvh>mI-)f?irClaT6_H92t7eiU`{%7o3c?(_;gSDKrv+HJK z$*Zd)VRdsW6fuH=$62;pvN3i<@rXjr!^oPZ{ny&NUp{@TGj{ki`MWsyU@H48^~urT z7RG539mvK`uG>_gzCm*+C_DGNGl$!$1kAEG%nys`=a3)B_1Dn9gh=oFr=^6`$-l#8 zG#N{PDC$>#lce(dYa-coEXIxwyfCgUb$V*4i=xlZ{<5bvbZ`kvhB83{mgNgxxT(+D zKg@lFS5({k zH;RA)qJVVCNT;OIA|c(~A(BIgbV)Zz2}pN03?SXo(%m3k5<~Oux%Ym5f5SV=wREA& zIp^%XpXXD19}SsB7h04;27xsJX{=kv@m(#~I$M=L>U%N`bza@S{r^q)em1~&x&LmZ zJ@~9^_Cx9^Sz6=Aj6)Xap^ih$4}>>!;$=IgOT^b&0+xZm2@n(#({5aYI9RX(pyor0 zXl~s?jc0l}nQTjBXMafT(lH;tD0tMa^@aYx^gZGR=y1AjAhq@js9eogf67yA&II~B zdY<-=-zY~MH|>vbc;yc{ke~{{13;!MuRbqsww7E6@Ima-IABYfCcg%^@XA~9VU0cr<(HI zXE>Tyf=#(jUfSSp6()|=Y}$Bb;+dvw)-%uk&i>H+7g<#H>z%k3*Jg3t z#m;gd7+ZXz;XZkf$vdU=Jil~w02w~|1C4^>beiazRXP2qMHP9O#|AMl`VS0MHcshz z4ZHS}&Jtd=y~a#@00~iQRsXlb8fbhk{#v3EwLkye?C|$&U=yCCeZ()Q^kpvyYAUF1 zKXFkLI?A^J8+T9S`K5{uHA_|Dm#p5qAe>9F_*q^ql2y{}?oW@);g#CJCyGjaIpNVg z5ja`gWww3D-gRvt9u}U%1jW^n>c4eWQ>;$*_NpiCm@KX0zB2j1X8OC$*69i}{Ff|` z=D|m^Lus#q5;+0xmfoxBH6MG<`;Yg{(m4!MmO`M)h`Y1ow3imsoxb??>c55*rK{-9 z>Zrc7xu7J-Z`Vk(R}CH7OmNupOsxQkTw*;>fnASc0|hr7+XzR2LpPTJnEZYt&+L8W z5|%Wfsue??{D>bJTJs&QkhwyS1%xIC=Bt5T zzh5O+{o?6-zwe?`l6+cmjDDTGwAWv#>Tn{@*6~{Hg4Ffh#zQju`q8!X!owZRKx0WI=f4=GD6I!{F*rre--imvZE`#g^ez zE%o2^|11cvk?v2_v$Gfi;hF2a5wk*CSwm+e6BX9JtAorSYNyJPSLAw*sS2Fw<+|DT z^0;C&Ae{ZX?ysjni-m~v;ZjVnv+!nRy%`K_W;nW^Y1l2#A3cAzyp%|3lKlu; zoBVdZJu~om{^8r_*ygPRebK>G)S^agp9xsL&ii8!^1bMLFJsqFN8eNO-=RXp-!wGe zQ{fQtVP;whGex~KGN(+ZPDBUq&HLxW`4T6TvZ9g`oFhErEYC8*O#W47Xxnp%e_;)G#v{7(QCKK|=9#NTO5Zv;rRSBt832(kTpiV$7ok}xt<#qS?`_cOpT8x_be2OC4(B0K#=x z`jgC$-FNMkaEz83RbBO|@H?o>lykd9vzaNqrZ}l_LJiH$y>;q~4GpbJrz-42*4JNj zb~Y-`4QZtLo(kC*EkU;JqerjLu^h`Q*Bzfj`pB8c_P#}5JTW&n@5b_{>wlh<*rsD) zy!To{T87GmZH;!gtr@vT1;+Dt3V*ynm21pMs@8piW z!R5}N#hicO2NNlN(Oec@8vGBD*MotbGc6^-u zUSk@=IPjflwmLox_B`p>=~VQ+1k-@wrvF?%07GM=$dZv9KB=E^| zdZTRvv?uqTA2Gl%8J`f8v0acJE_t<%#*sV~e5NSJ>Dn%n{MKP{+7O)o9EDn(eDbax zk3f_tVe@-Cq;?!!ZtCl(*l^v`XQJnBVjf;%Gbocg_T{WBpZGx)N0y=>KiUE`fj=^e)^|z z+J9FYM1`Z#@K)H0h7Fu0Do)M)#y2kJ6~>4uhfIvAJ;5!q9oG>}p?Xn1yy6XXu46(X zb`9_B^!fO~TF;}#`{@mcz1rLW&?e&7aYcdJoybND*w(Yl9)iECMa0io>s z0ttRJ>5}pi$NpBzv7oe|0F7^44eFz zI5;@?SBiGXO(~15;}CMNpSD;K-L6Dg;wk+(nV{DKq@ zo7AW+n#)x?jw+_h__$K&l>K||=;)~J*{tlIkTa>gj|es?PQXzRUA^OwTm;>YCZWFo zdb;Bv!`5`Oe_M=nh0T&7c$Ho zj%>5~OUTpt{02|2@v%BlWKt1Ep&Co1_*07v>wOD4mEXFD#~-;lIG%jE=f=8=FIyX0 zK(m-j``hqUa;=d=M_NLn^EOae>RLlvGebp^H3z=7CbW7SlNS``9-h7aQA1O+ow^LS zFP@ot%S}Q~u5IaqJY;!Cw$b^rMW|!>+v}YDP*>M<@Kt6CwKNt}g%8o(#^`<~Bq#gK z-7gT#FM?rB6+j5Q5S-fVoRTBdH8600|0WMbM`kUj^BbzGd}}lE4``00)8Aid zu9_p|MuVSjxGjU#JmOiBBbu9jn$5Ga93Y=l@CVe8IOe-6s&>!xVs`|d3m>Kmwd)Pf z9DCW^1(xug8*Y{&Yn|CFRtt(d!^H-7;<$D;^{NZ|M8Cn8^%CCLLzI-2?e11pi*;Mb z@`m+B8M@75J{YZ+-@P8IF!-pYMSL@KY1~^W1SSx3Ne}(bwm10uOx@jF-ma{WZ!MPA zr7X_2yTx)R7b#~SHpzWaYCH%M*=E=b`*ezz_KK=K>{#V>nw;Nu)Z}FhgDS>CDrBc- z$S$myc1TS{Wi)*EN@J@^%@CrV|FfpZaOm`yPCM6cSrAU~C_}9S%VJKJT!<962Mdq$ z&#H-+!O#t!k8#3VM>?8FiqfOh0)e%|)kWisVXfjzxn2Sw_Q|;k(9x~E{FlW^$DBJx9{ygC1X8;1_uoG zlhMFX83!6w>WOxF+``whi#-@Piu5*KVt7n$@dwD@NBRryp5jC*fMI{1VKl+!IBHcA zEphQT>0rCcV_C5n6`x5nR7Kzge;AmTlVsQFcKruhv{6XQodqXmGtF;^xer6YTJgDl zGNYC7?OTwbZ+E(JtvC25M>*5>W0L|ZrA7*ypIaTqt;ey2&IvQeFu0K(1^Q*czm8l~ z^gQo=WNbNK4(n4DS(3Uag|*K)*-6cLvVudp1+K$IyNH>~|3vO<-D_Ge!q9|b z3lME`D^|2Uz5{88dpnGNj9}R}>igAFor8K7l~O^p9-)9ydb?ZhdD!& z!>^!9I7cRj?3~T6W_qQ|8GG2w6w2n$r2ERz;vVqXVyar!c3*}*a19R!=g?EBeg73V zjACTpY^!|!vZDGO%T%KbWaFg_)y7NHAzkt1vpL@I3X|(anWFI1?Gw|m#J3nNVVAOH zYj@3Nc;XRB-T(}h^gM3#Byk{i^0|-Obq(G9v+rg$U)iwya9~GNtScS5>G{_Ev|6vb z!R4A<3wG%LE8i^qI<E0>qmhnkWGBG*Q4XfUB6Ux(^yMrq0VY0Kg`Jt>*)4g;-B#rbqmPy3c3& zO<^BIKbjeFJh9R_X$MOmGU<`UBxOb8txArz-WMNFTeb1}W^Bmti)P#lihFDf_sJbD zauZNcfBzJruv&!myj(jxy2ZE+2oqb)lP88RRC=)fsJWChxy6F^empjtZe(Q9ZO%Q> z@FL@l*nM-bMP2%31zT}zc1+vaB#a@RMPKBa1^JY?2Y!V~>ig^06lzMxH^0damus~X z(b0{syJqu+fkVT)m8wacTJ>24V4N`tdG+!hmF4CRY88+472`>N%A9^CwqBYv%h7Dz zfpybt7T0D(#P5!CH+`BXXAEW|5!fduEpPJ(C2+?>2LkbZ9&8Y$jh=LKX<%;CPhi)_ zE{gL6fLFt{@6Y}ykr)Sxh_}iYk^UK|@7JOwV7KIo1`7#)-)&pq$={}q5As=VWh=x@ zb=C_N&$4I-IL&mBe&;Id$1LR9Z5|*OYt^bM%(d>;j)0%}_PhWXq1m^`!~*AiT69bd zN)2u(7vJ%ri8Ga!!&cVG(wq?YZ9#3^t3sXAXXE|&I+SwByk&}zERUp5TW89q^}Ar6 z3po?|Nsj#?;ZF@#-dt&Ec=l8400 zon^KgKgdm3bg6twXfVi`oxld@fRJP4NV4|J1MfHG=5q%!fE4;o-hKY^<+quR2bJfm ztV}k$9sZOaY>SLxUg5myZOD!TCaSai^Z7+k==<^e?-$;5nYsh8orpi2fBX}Q%^e$h z=Y6rc&SM-XESk+0rIBnHho(*!Rq#BxtGs;hw&hWD6LU;Tr>K(L7qCXc)vv7BzVRB{LNf;WoZ5b-^NKuUs}$5WzyHbCM-ahX0#11!nm zs1e(ND9VP6Vk`uv5*T%DqiqqrvR5H#5eJhn6hbu1@UEh#z%PY7(diW-l&czy4D9W^Vnbm`hC`Fu} zhE}8HSWq=?gt(ng_-X8xoBLQnq$0FLEF-$aJ{l0SMoNIWI7|3g)w;=Q>u zK8v3zYrEZjoAKVf!@De>`@9>LOydT8pE-<;z4aje2rOqJ@`4Fgwo<(Y|8i5-6-5b+ zTfl&0sBnahOaRfvV|tM7eis(&CrafdBigrF?D6Go;>)PVI2afS8TdcXPy9qCi618`03NvB5W_+|S@r}ef!&kP^(i5<+1W&9 z&hGC9Q`5Rlr?>`!6B@OwC2Ios-_8i;{$pQL!+N)r+51DUTB{|-{7FQa5EHbl03EyS zq$m`+SS*>ZTZGQkeA7e8%>2~pP@ug%XP1+Af{`DB4nGhQpJAt7KhVXhD;+7UMX}FI z2ToZ;ZBWq0=IqC^0D_rWhuf?(^hP5JW^v%GCjS1Hm3K8~Hcj43W@?lIdU}cL^Q9K|lpjCPx9XzfE777P z9z}^_Mu|QIzbaPHi!xCmYe47{QLk`ltF6i zm=|v~G{QGf{&B>Kwxne5bffOHbP*5{KtfI!%rq64y@NyBh_5!=Z?}h76apoV^TxD8 zMz;59>!tS|q8F3lsSA8YBaH>+FqqOzsZALRoo*rFk7}j+=(Ps8%|H^@lTV+1Hs#+6 zT0&6IuO+QXqa^$)O{5Gq|7=Xd?1ca*U~wJ@jP%8$iPkx(Pxnf*Ml{~Ob%T0#>I&$Z zQd|*gp?b;Jm=BJbCf9m(uCllI<+NI*<$YFcHbx_+%Y<#gak=5U+feJnwx-{;>IDA1 zLpG{qS`YT;I6E?4(W=6B7jww)@9!)F;_q!Fsc^pR&!axwnLq%jN7P{=se)scK#@_x zLB+z;Baj5+ay5Ywd)LI#WqM=2Wnbg*z~GzDFgO`I>M`Je?~GPyog-D!TEF+M9`9W; zzM$7p_&wkE<+_6T&v0U~Ek;IB5yl@GipU`{txV4`7N4F*x5FqoHf-!f&Zx&m_8hJA zm_PIW1_yC=$VSX4#1ECAFmDQNI|=XO2SEA!OT>3ZY6L=DdMQGg$1kNH_^7{P*5(v} z;?Sq$_@mH-pW%<6k8W6^m)+N2r>EcLNVyv33^Uc3IV>OlFAE@<{Y8}_PUM<;}splqRR1!hS5AY#m69zYq{S-fB_+h5GYaxKpUacDvVfWjzCgq zv3{De7AFNQUxGgyNa9h1;ygjxnJf(`A#s0>?TXw0D|sJTx=(p~5HtnYrp7jhwmHtW zwoA&1!PWSPk5PE!eg2WFg+*cd-I8{;A@(5xp%{4INF64va@^@HS5c7S6vAZRplDm< zD3OepYN9Oqx)~1st$*ug^i0oRXNo6SB}JCNlbh+Q5gH7hGBzkZEB?M{^jEy`P2@; zWHSMJp<6zndCQVfBPaN=oqOO5kVZmc^G%LiLfl*cT$`n}INsp7n7XF}LS&JhzNIB& zwb>*R^AE1pzg-;I&z+HbLl4;$YVxv>ML5ro+djaSlDo>6{ zTXL_@uGd7FHZo{JxGI}FfVl$(KH|bfix6B*0O;VJs5fRDNNjlz#RdUi0^8jo4;4Dv z7Jtz#i)^`lZFH@L^nsqpCqA&%73oUxB#>mg{-T@&PCL3(uJoIlvV31#Q(ylxUDxug zD70C<;S(O{!s^;q@V{}pgwwSR-D_+8!!lb8^ho6uz5W@R_qfal;dX2a}YN5}!UHc#MVBu|_M8eY#Gc`Y3R9WzC?C@!8th=_Vlo{K0L& z<07~(Yi}}&R5PnX*lpP6aGJcrJz#uh0Nf%b&RirEv>LKldR3iLThFN>=%etwP}DOE zhI#U$zU2o3Clz zy?N!VSQJ4S2;?eee0T7 zg;}fMLLEsDY)2#?efKz*V|G&Y(#6(6Z6~{vbAj*>0>&63{}m2pUstXXnI7;yz9#+= zxamrb31`%gA0dH}KG?SVsMd==1CweGRuvV3hSqi%Dc_pU9{R|2N!NnPEIfDW*Ee9= zD3$Y>I2;!!PCO^-2==azQ8rbiOy*#rs#+&I{G9kreu-UL2%Z#%HOcik8F=qJ__%h1 z*)rKAtv#S!r?lUH2LQU=b;~so`kXcVR$3uC-J!pmPIXio%BPx{nTb++Ydn_5JD4*B zmrNmG?LdJ~tbcqX3wVKopAP55Pl~Fx6dl?4gwDL+E*eXH|++@*f6QUhn0i2w; zX&ECfI#c&4nKD(}>@s{N<_meAewwxG!s5x@Z6K@m982qAa*J@Z)9mE?t4Yu|Dl89| zeCCEz*o3o8^|6fw*SLz*ajM%DqqY(>E)IAeEeQmEWt|Jst~K+UWG2Znx*XM_uJF$< z>@xQ|cZAUa5yjwv|Jhl+*gr1DU0`sw)j(e#`kMH&HgG-Tu#}dIdv$7rK2p)@3NikG zHOey-^lOSYGJ}=AfS#z?^X|Pfr1$q|HGlI&`@GJY;H6IT%AXy`7WG?tdZL}b6353B zE1<(N=~lG8WSyLuSh-D71ghPpU5N9RshGI4=!;34E8KoHA{O&P*5TBa&b}J$N3}NN z>G%OySDL$*GAl#`p~xYV2A8bY^~dfv+#6GW!=8!j@FCYU8N~!bMR(IE%B& zVbQseEE(FU(|v}Z*w_~_Qmz2~JT?PD*dv*c2#!THe4+}T`lGVgXwYL^XLL%`7ZOG}7Jr%; z-|(9I>Gfbn2DRUGSuU&Qve4Yakbnbl$_-0_T9;heH4q~_p<|%84{r3CFkifd1HKQT zq{I$&pG3D1vaUWdUR4badYvIU`bJxPv9p=Of@w)wcEVaL8s;|{tmqIj_CpAk<VDKy@jkd%`XP7^kM{b`k{sms`cyHwZDouyrl|2maka1D%Y zdkFQ;?wYyr{?&fIVoQpig}_RG@YmVjreuJGKp>--xRN~Y(o%)cwc@_qbZUkX3yJ); z$OdXz#t-}&<`*ttmdD$juFT;{=BjCnB>iA3(dM3X6)cEgwLrVOLsQOW{cRqWoNc+u zT}O2E?9X5_0{y#^X~$nl#CYD41xBV$TJnRinAfxF9n-?$Iti-xWof)?Lp3qH9;kG< zI5s{=P9o;1jr@XnauJkc;ZAO{6C4!RoaZ($<%ROb= zJSy8c^zzgB9If>tXel$Mt4-cG9th@7wQnuBKJA#m@ahm~DO&7)Ar(JN`_JI)MqO zR1m&QzzQ|y^Ei)g=V>M@Q)s)f{XA+Dr7iMTvspOr4wOm79KbpmwIAk4Ik)WwLVm4n zeZ}8*rF?l5u(&!N2NZAed0xxC*O(AcgE%&sC(OeTS77ANPN!Y?HiUo`y)0AgC0k5u zjsZ$jSHVF3$}K|Bn)iB6mG4pZAnUp9-8aO0>$ZvR+p1mzSQ$1m*0Tn*LQ-R zCs#-Ju2xuW&@iRm7cCkg-l_x{>j9p~ih)hwaaN!~RO{HZ3eg)Wuc9cVmtJ@7KC_ZJ z+|!kw!ILLuV^dYO8D(O>R5iCbFofV+*(M)nqygyaF46$wHB#!p!D$ZOL0_omR}%fO zaX8hoCEB0U9)9;qIM2wVs4kv%w#Gap;LI}X{iPDRJf{04!}~}@22&_BT-K5x!?nrL zXSV<2@s@xA=N(TY@^^fEhPh7@rt|ajm}k3_?{0r`m4CeB1d7sXdrr|?`ymQixs)l3 zfpBdn!$}+CD8~4j#kGHLsFPTd=U4ij&^Es~paLWL!pu#bF)S@b<)R3_m{fbX?O^SI z{^B`|cV+9C12vMm$oZIE$5tCmNG5%L+-KG)9jliunZO*C(l3AYN-V z+8;+BEaLWNb*HN%f#36OAs~mZ!O4#Ii#PIlb(}LZYK8>`0k;3Uj$0c0nc)YSw9|&Kz?hg~fbDx7^Z=n%LPlo1 zu%+M$Iu_n|DR|nY`ho$A0(=;S&%W&I-zMXTwe!1@GUe+6AZ>TBPCj0yEuwJ!wJ5QH zt73RqDAVL5z-O*(X)p8^Ewace1~2uvx|&l|Tx<<69o&}i<_)Uys|81Qmd zfW&7pm-^O${C>eB2++C56*k%aj?DGWH89E9!p1C5Q6b zDm;E5xyOGA3gJ8hx=sxgfGdN`)x_ta{Q8E{Cf`Ag2D@npfMwD#_IndynaE7^iN7f8(?6uCQf-P`$;{)Y`uU&?!f4(hJVF(0#$L9~P=PHxq0q@I;YiHssQsC=H zkrX)xyf@t`4`D{qZfOL>Gdujccll%a zuUOD@)Mr>gs}%Vlqu7wSbrD=e$Yaghr)C}^9beYFK=He*A-xXC;9|@C!eqckLPDWQ zb{~+`{JHF^K0KWNV8yKCK_HecDtbiQ^Z?ZkE(XxDFD}@Ge>(#f+V<}7iEB_8r=elS zo%2~bZ9B|)rGKa4GIVN-aN<6?V>VyAfmr;W{hjSKaQMOq`E*y@b1%ZCN_1rv8UQIH z6fmR#7S{x<$sV4O&UUhH_k0|@-wQB4G=zlv@W6U&p{h#U1EA}N92~)IMwPGM^!)Y~ z!my?QDl@PZT7mFyHT#nUxR;PWvP|u-GHq|W$p#YEd@1_IyAC`8Q6e=`Bt2S^BKD5X2)wbwB*DbJrk(3YI9S+F8~-rFu8di~DSWv$*GCt%lXTxJV=wq2r# z*{zcPwJ6>38jDnU?$dSJwPKd1TS_E7?Vt-jeKt%MBmin^VsUVB5w6-P(OdDz5p&bO zek;T!l|0TDk0x!-;~K0(hMfnA<4|fBR;~$v%odLJ%=g8jysT{3xe(lVY;wV;+kTjJ zZxiuqZqXTJOR5cpSAl0eu#{_*8V7p^;T}h3O!Bc-Pl0lW9=&kAe0Mmh&ta;FYogcj zjXoYt|K7gU@oqMD0*9CXa)pmbJGeKCrp}3ML$Yd%5eZgf6VwdWxK};0AAsTsbg#D^ z7S#aO>0HCPC#|QwL1!X)t)wJo7G4V6B*V2?F|)a!k^wQZfW)jgO;Vi17kA&Hh2Y{R z4z|yel1ws*=KNqrfhU3@0_Gn`$DS1w;a>Q5=Il?xz5r|4 za3REnkliR`Sy15m&WCaHJ^+Ym2ARWkDi+9x!a~5vVNzL(P$EpatkBF*>3g4JDB z>5B{#>eHl)8i|?$!8PBj``GO?#T4Gh_dNf+o&CHOKnb<^eq4Z*)Mq}mYuIRLX?)a@ zK|1RH-E0w=H%S0+lGnagB;W?)u)(!0q$Ow(J2eZ^77*IH!^xA)PqxRtJrD>dd$|3p zZxmvvKN;o@PL(;S$1(CCUPedf z2HI$#vO`D|cyz9gyWa|qk-lm$b;F)YvspNM`3|iq{vsogKO9!L7EJ-~2U}U)zCEI& zD)tNmr%6*paP@C~a>bF?M;Q1SzNal>BQw2GXBzAy(~7=NKR$u;wkG|t3O}z* z$B!NW#k21;0jU*08xBu0Uy#HyyKOI!IxEDp{gbvpr&O2T?a}|8-a9l8jz1v>*P~G*IT~nVmpDo z*=BTDeU2-MvNo8bt?S1fTsvc4znuBq?r>hOn7S*pkWW`zM=h>i=>fo8?y_Lyo4WoF z%gzlArfN^XM&2j|_zzJ76<06Ss=Ch*2($Z4A{K#EikG@qflLNKZ2H+-7_7d+g=~mF@Pj%;HlGTwo9{CMA@QXtoPJN ze>kngu^KsSF@i@-fX+Q3ZFTk9as6d@v@n>Lu4feCo$_fiS44+Ei#}L5@`W4Rmi%A< zcMdw&N?ME4OLZ2@rf>xO5Y@!MmGlE&VjNa9XtqV^9`08#V@l4=7 zCMdyqgAhP(cTnoQc9}pQ_l=7?U@QSh01(%Zz}jKs;UO-qDF@>5GBg0A(3eUWpH_xT zXagecK;8kkI2h!vQfLu4jPS>sl1MOq2w>r(ucNDbVu4Lxu=;1m=am<_?{*l_~9+!~Q=D|Fx*Re&eKk+mlyp9QXIc>?k^W zVZ<0F5x^?H(60|b7+M=ekNLfbB20(%S7%PtzxA#HqK_*UiXULQ^8Sc!JtCUA5jbJ> zGBXVLM3=-M;?dr(jt$W8!(si#b^vZ3M+W(Tr4-_dFAk8|i*q`p$oB{N`n3Uz`eCv& zK;^PKI`pzV6n@^`@#=Wh!) zYtj*&3KC#mLG$%F-QdlqGLQe(4Tbc~E3=X*jSK%98V582s0gHs3^jMf>7gZPCE zMlB5SIIyud6u^3#ys02FVR+N_%WjN=Dhdf&Auznzxw+f?L$y>mY@(1T zy&jcyFag0_i~#x+B8Qbf#a3QfDe774ZVP5PK>z7V{_L(P96UT?+X4V!>1{$rQU7;O zGdjPM*9?p~)bm|$Jptaf*;%R0`jq*8hDKV@5KWG_Qo3nJ0m%Dz{3{FY4+J~^ zP9z;qQ4U&?fwCd7d1-P?ah58|o(-Z&D*~UO5@>E){4}td%s4lcWNxC4qf~pnex5O+)7vv2n0{H2Z!MStM6Gx%7>?M>OsC3+EJE-m#@V|Cc zG&Fq+6da!oa>rk?2|o{N>p^1UI8eA4y!SCBE%Z{3ekNaeeKaP%C@HzLQGme zu|RgJz)Bn8Kaz0~hLem3NKmzdpy82b1*HfHxLmV|#whPBZnEB9`mOV-+P<+}PRf|B zwavR%cITbspr3D<$Uv0a{|iei;>TWWLj*_}=D5u@(2T{gL_xD;*IBA| zsMVPR%EEg7SQ`k7tpPbW9R{S5bic7%G+{&_q1t9lCQD;<^wjMqSrp~};?%MJV1-Wu zwCb*ArJ()mr^wtN!rOkNSZ3w-2a7jVRiPQPDk*Uu6T=A7n( z|4ls2cYAYA({(0q)QV?{9aRU01$YfR%bPFSs>(;X`J5T1^O2XW2rr!==nr@_dET7oJ43>MIO9eZ&b8|IAScyRf`Sn(@TX zlIcchWS^r_!P9_e+u;v98an7Bv(hWz6mMy2nXT8$J%NLhqdYL{e97=o*XvwOo9?D_ ziBF+POYC-ttduuOrOMSL4vyjW;_C-WryQM%-;-)6mH@ay;|V)xL>^N_$}O|kY`65ekGT-?@Lm9V07JW z;&c7Y+~B{u{p-;qy_TnG8{mA>L!UlJV9*f)fA^=8IR+ymWB4YI<7$}Bsxb(7+LqUj zIfFBs7o7-2JQIs6!@)dWL#rjSB2gJWAp6~9$>$I6gjA%n=`~`iyUI&2XV{qATO=)M zdKJ&rDVrn#WR{^I>{MfH9{pIV*0YKg@X(1m3M5iNS7LxhggdUdYIfwi+y1xd0viP- z&}e;n`|;z(jLH-+bTY)d;Spq7?ZCk(ss|b6@}q`X38dFw4CG6?$Hc*8`*hz-+OJkn zc3ExR#qT>g3iu5#v?Rpi8cV%wLCIApGOKCQ7z6u+24CbuzFha&)+{-grkrM^KNJ4W zxnHCS(3zbPB%&J9tNk|tgS||0z>&S?cPVNk54s7E&&qoT%kTuQy(qr$-GJ zRtJS%Wl|L~_J6xfumR+I2TdBm{gi3GOP;`GLv%BAwRU^`Olzp``jO>+c^IFGDX#@)#V>n&}-%1UbCgy%0)C#R>j$n-esZI*k0}l zi?-DHesCHx?!7VhxmE+Q288-Jy@uq+f7Fxf5IWxd{6@vWgO;87f0bl2xaal%vH(Pi zfv~>F$ot8`Irwsw5i&2FFw949Vx-(fec(qeCK$msP=qb6(ezrF`vE= zWI*i^JUI&y%?I4szsUQ|Q~Y=uZmSu85Pd1IIx+51BgPY+>ICSLkb6fx4hr(za%F7KS#Ze?6B z?)Lro3v}AC|23p2>=OMZB+Ku^U6Ewk=GCrL0u|#GbMNZ#3c_+2-xOLMW*Kme{xZC+ z3DG0~hV{uO+3`Y~&9s1JfHa$Ws8oP)^l4B#;5cS8-)a+Np)T!JrdUSRK7U3>eU_G8 zDGPcyo0Wu3Np~8D2Wh9#rHU{x<0ohYrb@2JfP43ON+9%w(q%E2%@%l!L}>qODdHc& z9U&@W#}9vJ@-KJ@J^%*BY$-QUJU9pBkXILPd(aOTfRgV-tOJOa!cP^pl(i{wgpsk| z10#lmLQW~H4_Z}S^|~+UeS zOCA5-mz02$#4cKn6;|{$=SbtLyOxPnp-LTgbvNS@k|jP&ofZEFw|sexPL$$ za{u+t<0mB-7hWVe+N+mGG$2fh+G2ml5NX!3MUnrSr`js+#v%tq9(DvgWf3_puoq;c zUW5^0h4=RM*1?zAfD@Sg*G;F^jFA;|9Jxh0H*$vC^u23^0t;T=L(B1o{eo5D9&%{RKBx zRIBC-3@9(#?Ws*Fu4O>Y&y^=K|9vS2i4yL~EB0oL4yr6rHoe8ZIn@lX93-CbHl<1H z`$xSCP;U67%A`@r-BH7LFL2??p!IRxvi{W$z>e>MK*qr(Cp$Cyr#qPlHx@q*>#3XJ z5g{VwHza^sH2*2y8icDX)Yl(Vri(@^k&~ksn3&|)V~Xwo`?Jex8b%Ng-h!7OsR0qU zE_h11zyD3hOOUfv9aoGWosdbK{b}u?U}uV3B)bj=$D4nt0v$<#PDSy24Ws(~?GFj9 zTD!dYTL1??g3WSxjInC2KzkZZx2FI)Xxep={sKa+D%0IX1mJwTUv&a!xS|8jdNZFD zQz4RJ6`?&>nb6rOFCi&Av6Ysc{jsw-T*P+EUgxMKz}!CGaA17t_{PMqqJ`|Al0NEP zQ-cTMH?b?TV)yTmT0smRD0IpJZsh*+BR~BQ7)`^NDUV~?y7gLS)D>`gv%c{SrOd~e zOHzSG9O`+mYLbMg?|>Q%#5&V$BBZ4dS2EvT4uUn{i2u7EEGX#9?{vLEO#L(-Cn8{Q zmjg6e4+F-nl1t@xO8>%l(c4eJ zN;H}|l``qgcQYzIO5SIE&K#oVr|%cB*FO=)=c8pI?0ZviuT?S5MJ)@dbVv_cxhpK9NNZGy`^ zWid4J!^(8e@$l)BOOw3BD=YBygeiG$$bf2FT46R43H>`|Nm zLuM-_$%OX;VH9A4Zje`qh4FZtASIfc|JC4il71#UYr(Uut41qWn*4R%M zWSwS5++lq`)4J0AC)PGgO&`{G%{n_fAyrYS6LLQ_k_VC|=WzLK=d0%IZ*xd&=^TT~0IeLlJDKAXZF z7&}rMQoYpCpt5J@j9CqA>VtaaxAZl)=VPFtI?r&Rp% z!*^{Dt+ysC@cQCdB0748mrpPI^$$UolxdiTLlE4(JEJsjzAB zOu5?(XYH*4)8+^a)Z-^^55RrnR1oG!yBfCt^47necO&!@g6$2xE|s9_4nZl56u(si zcdAh-<*8Xd+(`fW%~}sa{z+^G;weMZ8=#iI<()RQ;NLDTDJh3`D2-c83$w0WT5F>K zDZVMpB*f1eDh&sL>|!lm65!w{&;CJVsM)5_>`i5Ui};BG$y-9Qw^s}9am?`09=}XA z@#E#>ZJ@mE{R-|2mi35UOZmpI9_fC|A^@w`8?n=Di)>P*9v;v72Ae!a`l3+H3XszvIbm;WO31y zb?*WRdwQOtk(7F2HUVfJq1-7~=$?@ZouKz4U~pN537SBIlONzkq`a9ou?-WuunesY zbcDf`+buqWD-Z4WKA@jQW{R85*A0JhaY!bUvXM0r=(SZ9pTKF1z1Zwlrq}}VvxL{X z?!uDUHo91i!N8P$B8C2-b&wCvW_%~rwm*gvC>XE30M7B$MdOl!cWzIVUvolpJ7#(nO_KDG#aARH%yPR#X9z&xXkg(w63_ZWf6*V2&ASd-jpH82nz=HS zU=NZ&NzfcmHiwDN#yIO^Sir87JHdlIn6AW@J(Fgf%Lij}UPt1*p{rfa?jAqU;%jR@ zbw`9<#s|NH{@Xz+vuDwzUWSoQ4%1c8tpdFU4Q|zXKxX*}FTE6)xiIUaw%}KMiqlzN zhfWx07vraRB8S!rjSLM-72WKg&gI#c*LJ|dr6*uXJWh$Tb}GFobFWe6Qj$-#eZOOd zYR`QR8cq{J^zZ3%Xj%Jp_|AmF>>J=w_fJ}@x-MlIcsk=TV@CR1g8jDMrmP*ubX)X3 zJ{wgFfX-l%@`PKbXqjK8Ak`O_Ahaa#+tHR}ejlDvU}T!`+7*|fO`bD}2I%bH%jQa} zTk7kHcBTqZ)u$R?$4ETC zzys#)+;&;O4F3R%hgMhgL_$)Md5Q`Zn0tLsz{Cpz9cU$c>0YgQ7@$-g+459{6PmN% zjgkVQJ)Nt46JFTl^LM%43HwOFaIb{KFvnon)uc&3uW# z56=9!6<4rO_oh7_Pg&p5PSH|x;RQxqWem%c;YGNB-j_4?hj^mMJ5_hKy}cdMadwcR zwX)TFGHt({AbyP8TEKCKuI{h_#HUV0UxjK3%6d1&5u0+O1#W-F-aBxTv^H(@XHD%4 z!v*4e^#+FdzZ+s;BtD`?K>x`FwGPPm0ITNZ`ZmY(dL?%-Gqmt6Fdx7ckZvsBAkNMb z=uBrv`tzLod?|oX%nqBs1>p&ygC+F1jzz!x8Iot3pq$CWLb*YP1Wt=@?QWo1C=DivtI0NCi19tcd0OH3jE2@a}c0Xc=bC$G>lKOSdF9qQl0VePwMPQUYfbJqW{Kb8Si zod9fUt34DLr5mC-vjoS6nA8I*g)5HrH7+M-z6o>>JZmuww>zm72asnDBU{vm9v^cv z6EiHZ=M6M&8?%{@zZ@^p#RF3v2`Ock+rLTII_}oWURtq5&Y^zy2R`dC1481uo>DBo z`^$kSkl-_haDr=XFHw_^8uCx?E~UNE%bcuUxj(VUa~xbR6>EwRzUg_?W_&Z9;Xlib zkB^_PCU$I>E}tqG(~xWC@Eqy%wg8uOZ-N#e4eVK%RW@v)mdc0?_z(fYO$|mJA@v$> zEg$~iF3y520U_cdIhM*vfQ`*9)oL=!p2S~L_}|N5XPK0M>`kA%Srz>C!utQQ_uk)d zec>CZ^dd+hB7#Kkl4wB?A<>Cm#}K{uI!YQ6qDSw&6TK5eh&FneF^1@6bb}e??nyr1 zTmFE%)?Mp<{UR&o%-Lt3z2EY@&-?6AFRbdR1)d(HHHmsb>hD=fWL|enX-@cXZ9M9xuxZ(kQnQxFbv4m;SV78eR+(o69#Lk|W>!q%GUW{XbJc zxVcEf6f2}n-ZT+JnFEzZwu~&fhAD7ikZ`J(^Z}>w#2pb=3hnIhc`VboLT)>%`g2PE z(3m|!=Had21*O;ctcRZ1*^devx*+8Huymd%Uy(ZmkqZO&#-EC%JM60~fjDq*;t|lR zAb6PD$5no$#%9c|-=0VkTy!1G2o9aq@E92{rLes+oSOx2yDjUgk#r;XbI6bR(CBDy z5?aMOvWQwI(9IH-8qE|blX~ftDm&P7S93{-hGyNB_5xIMe#DFLVb40@=v zHD5r6ms+>?=`ocvWvPmncx)8~y9Vn=)sEndPq8dXJ0?Y=E;~iyr%kTPnTLEvX3P-{ zcMl8>euk&B1yMsIb+K-3DP^B!`c9|*)i0gc!KP>B{)&kU?r4Fn);*?Fx33+dYaOfqbTfu`Z<6(J_%%_a(K?>qv z_iyPVzV~r~a5nD7c$>l1R5iN7h)SbGy@D+-;Je9PD6vVerJnq!b;EeErh4H`I*>gm zdSq;Go;YhQa*0Fu!w1Q2Z1NWFueycr9gZ~9MJKItQ*MB=v>JOiP`pWIQEs0v0q&qT zLHPPUAo+tT$2;lk*R$55GUnq!xc7aj&6hUecxsQWCK9wF7Ybes+h*20Kq&pisHB8I zg+^#3+FFpp06rJzD58hCA5Bq>JU9^lup~U|el3%^uVdiJaGB4z#Pe&{Bz1KY0&cBA z`wA$?4a7a>zs)>374lJWK+fdC=Sw{QdHGRn)4^jIyQ=VJ3NCu+I~tp62^3CaHE+eq1M2FIH~MD=mTGaEwpE}A z)Q~+Gly!J`vCjJC^@QAW;`9>FxC=R++j$$$B+u7tg@7J#{cE zR&)X9B}OayVXeNiqc&o9v0f~;J>945N|ni^RnzlXpOf!1TJ3Xf?JtL}8|7B{2jsmy zl4+mo3{A}PZzUyf70Xrl%!@OkcgO`>ixcnB5N7IwFo4p zG|X;t#kfNoiab2>$S{439$RIc2w5*^r;MJC*#H_ z<*M!8+~F1DbadS4XlD!M&!6F~Z}1rik_UG@%TUty*);pYAO7Us1@-zRNW5&6Z_~)hXO$_k{XyweAL9wXryJjV!$h*t z~~q8x9WYxUGxP7VqGN#Q2sd>vS* zqWEs)&i-~pi{CjWCWuSJ0}d9~wJ9ZHzoWj#+QZpYs@7xo$H#YaK3uxMm*%eJBjy~s z@&vrC`0GRjby1=-BTH)ar}X*^wD;GsahihsDy4Qb72c%=DB%J)rpD|@crxFyU3hgo z(>WB)@&gTZ-&?Ulq=H#F_LmyInXf%af1Ql@ck3-VH*s;2x&~;4=T7G^*r zqDrzhx^p;nWs@pRn7QNE|2&(XnA*h48RK8$ozniqSJ*hS;t~Yx(qj7V>s${nQ7?6K zZQ4at;7pJ1bLrN1+tM(~mLpGG^a2G|UF7BEjSnjDvRb1T*&k?kWj?uIw&PfBH7KP~ zs6Mbxch|I50W=m60a!}H=uv9|pJ~0caoyR5ZviB;AP1flHM_*~oKvdC)uLEWS5Uby z^&9o-Xi146)KmA)^obPSn5<5;8cZHoXCa6h+w|dFxR*=nkC&GqVpnvIyzqF+fi9y1 zi@_m{gzY{uD(+jL@x25P>C(D}n5ry!`J)Ik`;eh}dBRx9o{XO3c>U7Qbm&*s)zLD2 z`y{2@47XQnKUx`t-RPBAlz!&FYk|DKM9fZ4ub)ldrU5FmM>w1pERR;i6<1WfIZ@m$ z9k#X=F}Jf_#`f^x3nuzD^#Uo!(R>|^=H_PIGOJ{`fA&$zd_P$cjPS$s9I^cQYrNROEYTe+Fi2Bu$AV?4vs2@w&cFWSS36E6Fx5 zkCvHGNZby;lH|`XJFf*SKo9R0!ikVmHheN6QK%#(?VI zN%le^53kj>jbR%0P8vI6LPaT4(=G&t)>a1kUtaprMYxt|ODxFtLaY^q&vu`F$GSKx zggpnf5|gMHCQdMT242J8OPxBLganlSe!B6?!)xPJ!{+@fAqP1i{1kxTDbdGiCs)V{ zt#hl{scxl=r6eR2G~r1thQ6%?iMaln2~rKt332kC`g^^{1&P}w)@}`{p~;_l*Y&{U zY8L6TG@z6zx?inM)p^i}c}-#VKYTc^(PT9})$w-=y(`Li4K8}`e|@!%CE)jX=dlW* zZ?hi-6EX*p`es0u*o=%P7Q^!w~>@oIR-q6jDW!2@;UVuB50a;Iy*1VGyM~!eYd9jRyESV8zb70ENncxytmZ! zeA_2k%vqrG*X4PBPFsSiG?D(eSR5`Ic@w-|>3q^aBPgKyPkY!W7~JQ5enmP?Tt6rsgq<`PF|0)^D;4lO29`mJF zb+o?(j@QXVpGsCiVQ4$bQP>%q1hzHBE!x#+$A_^0^KK&Uowb!H7vpx9M?=vBD?g1A zSxFQk9;!M!fnya=;!oIQ?Uhc|m2!EDG5umFV32ATd2~yddNkM8>keI($T@VSAi~uu zfD%7Hw`;#*9N3(F3hc&ysb^ZL02a8kFo(-X7jZVtm=5)n5|?Ez?MC^|W!v4>uqERSYH4 zuK_mA+0t6OB?jpX)G`)sXD~7|v5n4#mnNqD>H@~j7S+rFK{lUU;WtGnbkIJx@#qJO zbbp~n9KaabqAuuUIYq_MwJ5Q{7>o!>n7A*!5*n^ogdVpYS?&yo>%SrFx>!{G3Zr!9 z$iz%VOAX_pJp=pkA(5<aYkN zm2^5)`zkoLL=`v_rd;jB{kN~QWuC-VFBFc|kq27zEoP=Zz(a^r}gGpM&sN@@NtgE9)(qCUX5EnR=|Gbm&UECGSElebPO*vlqkl zFG5zwHG5A#t{{NT8l&D+8qJg5aAi|EIqDVCJ<#fhXI#5}edKWIbvFJ`xY9bEB{k8a z_JML_$|3qffku-P0$(%jRFUGjU<<-tdX!^lXlie3gnUzjyXrwdX2@rw`ZZ>(R#41A zNMK`x3K)I;Y&mlH;FCxA$Nv=4!&wv)o}wO0MP`W$+6@{jH)hp|pU`(wR0IN+Uls~> zzyW*@rsbq8P+@ZryJ!}GNvxK#9H}U^9GmjT!FJtFYG<@WcX#x3D_t}MyiKSY><#H+ z!#SVxNu9vZW***zVSv-v)brj*h#vZ#KtdH=pXs}O=;7YC(h{7aDbd{TzR#ByI4dOL zzS$R1_R##F)HTsg5^$gv!!fKioms*U68I0yMa8iDm5=GUH!6D^9qob)DLHhtfzk%w zx`@~Mr^VAssXNn0Qs`ctG~g6HjvY-jKi2b;AgKlOj3*2-t(e-e1<@S$xW9<_;B4+n zTdbdLtePUm)B6<5kr2w)K(TAFOFNub8}ZjMbvd6RD*JZI0>fy>iA$S+|IbW-L{M3Zf?5~v%r)x1$2$i+hF*<+=XRT?%*Z{GTf%N{xdr$_klRC=3XUAr~Eb!Bf` zq|{>lPCNPi+cqo0U>z6e6gvfUUOL2Ek1!mpMBuL!cux)mbY8r4X*GwMdbL<9MHOTK z*?5MgHVOYm(TgUxHp;AyPU1Y^D>s>_WZMhA!WhwrKP2~scBhT`(_nzq@2N-%@@^GiiVma++m)1P3Y=a z9VBN1T4ZsH0&Kw1f?8~{u+J|CD#&1lKd)1@^@fAx?*6t>9d!xue_qrZ-q|Tl(IdTh=o1;q zZ8Z&-haeb&0v8UO-Z#E``)_XapZd){4Y{{lKuRODI24-{nGws z`0<}ax&sRD)_)#k+2fT5YBZ!=IM?jEC-qO;ov&cmQmQi_I20TI)Bc~SzYE)PsK5F9 z4v<*T_&Ye4r3Ekb@ zceUiue*92x^UloXsj=j=`TrHkouUPeOr%swLoX3V5q>6=tVAPnD>u&PN~Jsc922B5 z?|ZnRf52U}s%{Dj{oaar`4{Vhyq5O4jL^MR?ed_1b3pX)3PfJw_J7L}L}dTQN<_r@ zpQ9vt_g~~gMD+dBf7cMj{AWLie%$^q^di!J`7eYaB9i}azx?l)f%o$N;W_&WM_zdG z>up%dHuffA2@!QUM%;SU1gz6LD%rpP@qZuv_ew50Lo2#}A0&>8_#3bO@5=`B@&En? zIrM}Pbs2n-mDO#TrNf}yUy$bVB1_hcD}EdNDEVWQZlzrs15%3L)z*Krk9WtdcWGdp z_@T=@TNRWtFp;Mjd0L%+6alCQ^%m6%jw$Jm#sd?o?@m?=0jNNollb4)x>NZ7NYdqz zCFk<<^S$;ly)8r+>jN4L**Rl)wI>O`CLGWC~x?2zA5uIXB6h_&#x*}j3 z6BNai(A4#z>Ae7)*ZlkULQcv=L_5JB14EYsJ%dT`8|!FE(A3qx{{%eCWvg9`q(Z1i)mlHCn-u%iXofz$;DxIjzkwHaw_0-ypWm?F+P+d+ zSkU3O+xh&gUbEQ19wF{{%xg83mm+Np-tRcehb-@9eeVy`M_bOZ*sbq3_cvYjLeVXL zo7}oBjujxUkslX3isIStw}uzU%%(k{yFm1CiHXTzB&QEq?h@i39BbXCr^YA?4>?K= zAgz->cW&G_&161kbRnh=f1lAnrqU&ZEVqAP9%K&q&HDpUH^C_s+ONS(E`Q920xh48 zY@dM#Z!;hp=dgyaPg+CZaphRK?CW7=j)hseZd%RXeP_A zue=!h!N#&_56q-K%^hHH&{rdO;-js{tU(AGi)|JaE>u{#T_+IwG+XXozb~TPCs(t` zAl7;gph7%P@T7V2@K#}``M8=ur#>W@+{-Azsmd1bx~DkrRpbtEo_qJeb}YQbxWJIB zlseiu+~R5(#tBhH+1Vzp&8-ZTg@E2>%#hgrt_}zYY)ZnefT8|e7fCaRCM{!(=C4B0 zjVk;zAke!1*dp%F+?^Zvj~~wSuNAUgzs4Q2MwtC0z3D^SD>281yQOj<#zveSY@Pj% ziEks;0lD&COW>(8LkexIoj%C=o0IVs8!ZjclCTw!vnD_D>_)T-2bvF_c}OkYxxK#v2){K@D zsu8*Z024RxvhRRlw@|HDKm87MAEwNzd&J47?T#Ll9BxiZF7X*n5O;+qgRn~?MuRw} zXy8wF;d3z_K1OZO2ci|mjiIHvF8{IQ_%90;+jKtsI8vGz&qL% z1ekrk$idxe<8eQGfO=RV%k%BlCJ;`~%=h>URjMLWs7}H=jYqRdC{jFE`drR6m4=0h zojJVBDT!6f@p+~Kz}%q1;8*)nQo?i(sD3N|^uF})`%l(Cb!uPaoVik&)k3N$JTEYC;B>hp*T3b(@u_~Y=Rt+2=PXVM4HiY6`ux;zomX$8V3U`c{)Qs`Mh4L}Nu z7_l7d)~}^Y%RV2IAw1M#>jo(PH1m>BwhEv*QMDM>LJpe=C#4f&Z$Oni&6Y3lu9M-n zQ`Zt>rv3_0D$Bl2>Pm5JTId7OoR|LB3v@^2KsNZH`*lqJ9$g(Nt#Ag7NT%!7fDz4e z7pcIo2Wp0ffHi%QgOg7RNNuWObVn6BqbaUkTZ#tYHu!=C9Nh}oO7|sH#f)|xA@yUm zMl_|Imb^}qAB9&wFv<$Mt|~NNi{u8qpo7*?VggC;qcpB9@hp!D=GJd@>5)*TG!5nPL;_D0a&YU;*?<%Lj z*FC1ElaBMf+&8lTzGuFAAKI6&v|U6f+;aW?SybHE4To!G28icvE^-jvf9+3g zNqfoMqsF5zGxF7bak)>HZf#&TGoVvH-!aq6%X|J!mHiq{I!5AvWISv)wzkrJZO9cDC{{}34;s9;YYGgiA`N^o5Fcy@=kR^yg`ViaIfj7MP~V!|s*X+;i#9zvtJS?y)L4Mm z8AfZQ@8c3KwGYW}xx)vx-NSZcg6r7|HShvMC1jR+wPE7P`Gd+$>qDozWT}OdAtB%5 zz5yhOduUyVcn@FSG9;Fw%U@`NlNrWDQ&_{Oy!52yJhgC-#~WXtb;|zs%yd# z%DXQ~6w2XuruLhSjk^=%jm5~VFf(s_rdX&2<(z(0 zvvb&6EKDGvj#%HEVZq(kxB0#koF?bQF;|JY+J3TLQe$-ek@WHfc{0#5#+rGEsroVd5u2py{&8bd9vi zVq7hXS=kk4;O+eZ#31^Fzc29~3buhMo{!>LS;RJR`OCzXp0!G+7P@8XRXS`t$uH?+ z!0Xk>9pv)bipZ@rS&^%uBO@Z3{_!=-yUilLADUQgH5`*yNr?w;ogc(+6z;1E^>fX~ zSm_X|?KH5vaD|sDl7G}=fr&|s8z_+K$;SwbIsw|w{I>gok~f3H`HA%g+_9r{bS$p zn>)*5o)O??V_bGFmY|7quzBKRz8q5^c0AQ$(r^D&b!Urs*GJjjzZhs^|0yd z6gLKJX^?rZM}r8}%C;jZ8Zjq@9?fF@;`F`K5h}3lGJ?pfu}>hDzluEX^E%~a8_(V8 zx85!t@3+a~hz{oC!TRC$bfVcHPa()LTanRx{11hecPS$F5#ByN{I1=8uGv6|Qm3>L zTQa*%9LuTIG7Ui)IAf<$Nst^`?3??-%D{Mz`VF+_q?UsC+h@1KWH=t`DO)pr;NfyC zJ$%Y#BevA(zv5f^yGsk*t0A@hSX6M)FW`hUP%<;cOJ_L8Z*I_oJ2(ueluD4T)i9y+ zOIqR2c!7e`;HR1#lhqUu@ZJS(HN&u#2sMxv3LthN2p-fLG$*@McgiLepl>=mS(fuD z(QiNsvf%Knvd4rnc8=xG-k-H7LQOb3N4U~Bb~{uN)N)q*w>Jv_nF2T`R&3fDsagbJ zuvZRiD;20!6kJ9d>65jsxLBQLnBu&!%G^GKcv2I2AW7z;Lg$04GJ*ntm*0EYFxKk| z5L;273vhMW+ENicTNM%Zd-Me2SJ0kLclS6$j~wU7%(msA+v~{} zl{}X2#KvvhBb3{8A zVyYJyAR<)C$!I!Q7oSx9P$*HNSWi&ekC*j;%3w7-h7&|D?r4>wQEl8 zL3t;8c-#oT7i?c)%LZrB@(;Rzo3`_q%7ALt_BJ;S@$~eX2koy?yUwbt6_R}jLMc_KkUT1Z>F5g! zJa=%HSypmm!+OP&n2baPO`8nPnx`Qx7Sypyh$u$7Q;kJ543QE0Hkp|-5JD_ErDUTC ztpbxz$He3^u(LLQsixDMqpx4n(q8P>H&rk<;=Y=B<3b6)S4ZLdF!uiR`gN`-($Ln~Z2Z8jq(eP>4bNe&W-$FADi6ms?4U^m zTGaI`m#j|POzpy~#|z?tg{*q^5d|7m{3IkN+;vg&GY3NEiia>dRk)K-vyXwd@CPU0({`1zS`-JI-WP1 z8Qdb*mLX^=6x9(ASLF&nK-{g;1Bm-BV#4X;g?{1T%CK_1;SH{L7o=V)p}19P`}$djMGxUTaV@?Y@GbeV%%RI zlXdphU*37yJjYeM*V-^gemm)OX+-oR&gyWPa<4_uwI}2lNT)j(khr?y~ zdYw2mx0(p3B&>W*ZMeMm_+-MV9Y*JEh7j~Hq$8mgex}tKm8{8=oT z@F0N(Y0-boqzf|OWI(BLYx)(kx;mz3r&Rc47`vFG+vpNKR)ktsm7Z^w@KK1RTySm3 zfW#h0H>f8)=TQ1ZwfR~-J;@v?PT)BmE=ikOa&BK++8_FKKysno=m4E9cey9P*e>?3%$7qj6GNfY+s-(? z@ih9dqKT#CF&K%l6`2~t3}%0ylLJZEDoPMKqE{-OD?WCTeJMD`YHaT(!=8o zDtj`9VFM30srs8M<+JS@K+M&5#4gGdCtB|+M@bJEYmheYJ9aDag;XQ98c)iuXlw`h zU9jIosMHKyDRcQBE`U2GuRSo#3M^O4=?_de*GVhUlS77_3Iu3N$%1Wcm}_gT{FL1_ zjWPoZ@zrL_NqG=CTT+Q=%b5`ok%R3+neJQw#y}7OQz&J{`=g5EnpqCv8`OJfo>isX zVFyp?^p}@h&Cz!C6{Yw|Dd7B9r{DXi=bG^=CPez01+(~V!{tM1MU1rb>fRo8aY98l z_H^3al%W%5fm&$XfJ1>Vyo6??$nm31oiyOnJt3ET*-&5(LM3 z;2~r26-ywI&I3Y|6cwBA08?x*G`XS5*t{~sZG8;tm&ha1C@f<&UhNOqS0sM4r+d4l z_3jR5F1F~z^|7%`J(e){)LyPYQo6zIJ1iQ&cGm<(vOz>@1Z@s>cQrOaHhBE=cfg6F zU7opDA2|Ij;j%2qJNf2#Q%%a)@pZr}n6J^7WCYOibr8?7S9F9smw$FL;2Q9rpSXsZT&ss^fbjaH!Zi?x z=U929ZH{h9nU@lnLSHEb1tgWR^@ooWCkr?U^I5F2AwT+iKsT*dPN$Mlb88)pw3 zfLQQbEZs;E!8q-Ahd$=ZHN_qksT7yXCr<8~@e#8!fIdmbYmv93A{E*_GGQBFtCZO8 zj?p0eX1;eD+}b)htXQ+jBJ%S)nmj2lo^dhQA6X7un9DglGDT*(S%aKcw`_-uOJ~(n zVkay3_sTTc)`8a9G-~g+IWHtMdH>|tMPi76!0ZJ(4vdR`6mRqx-oE3wI%)s;E<81Y zr~RtCD`-%Ev>5PuQ6jP-^#WLw7!LrhR@!uI>Q9`!`O#xBC8NjH9*Vi zM7Kwd;BuISo~EpSn_Q~UF&_W(#|*kOUcOW0**AOgtv z`UvvaY7QOkQts`(9S_d{agoKBL`Yl7rLZs$a7wE?mz8feMx|lzxW_*JvXIVTt`=qc z!<8^sYJjlZiss(mRB|%AR11>z(HxCk8@kwBtI;B>dhf?pOSC9alTXb@J3bWOkOEA3TaJ}@LSZ6WA5pJKR_75-AoRHb)=D?aoeDn! zx27S(_*+uaAM7)*1vK)t4MM2Bu)zvuzrJaS?ziA8Ob^O4n5*7~J`hP)WePGs!s+a9 zNJCklb293!GAvNA+*q9_Z&v4B_j>yM3=^O5C5rCGvTNdO(Zh-*%s|8(Ef0&RjVjsi zs06hW7%Jz>rjp{g+d@$mou9$#Q|jM#2Wq))TqP?HSF+18_qrs{%n_*oHOft)DGkf< zi8eO2n93_5c%}4qX$Wrdq*98El!{drH)diS{N8=9`_61{gMD+>IQsYnF(r@SI<)$2 zNI;mEK@{3oNWLO~hnYq1nH;xkhtr2>9h=wBPY%zH*XV$6bg9P7tbVzEWGKK!G=qB` z?n3j-@AShjzUoBtAzVE7CrV+}T%yEJ;;)tQU=2&I$bua7 z3FoJip-;cPF#er@vzD*`+gnFS@E1043)QO{wkF|yorDSW23IOQ-(qyM4&cgYfoFA| zM>JD4e&#Uz-mdGm>eAC^h5GZlUCbSxm@6?feR2hYxbCT4+bDrb2x6sOEHBzyo80BM z>4NAQZvAON7Gs=_otG8qngtC`yxd)3y)3Cd$(fHLx&3dsqerDnl|10to5;!C{h9Fh zpp-F@E&Qg$3*~Da8VY9@he8z-pQ=E-NCk@T?2igMYk){R<-xu~&ZcwgpCYA9LvC?l z7&hrLN@U8KPT8Qi=@QWo(~Gj>?`y;kd*^P-MuH=OO2EGrfL2&vgyRDLqt;iha7fc= znq8kwgW31r#7IC75ao7APD-M05Zw)1|Nh8*WnY%gdQ-y>q6qTu-O}dFYdm5p)kvsW zG?$tjP#*6d;xGU6`S*cmTw?Z3&3k>-6}YzUtBtNd=$y(bBL9n zY`&AE5hnInMzEeO#~Ry+LHfO^|6Kv2dCYo0HJ;M_1U9``{-iaBOT}w*jt64KKp>tt z<27l~?RvCchT-?e+>`69s{86038-0(#9#Cq5He#r2S9KHWU=J=t<^`j2H|>Q%8_2v z)4w0f;MO*|5Y%@HvwK2_d1HCBn3cIi!Lmp*^A*#v6~^+Di2YLbhDMmmt@NLYK6MQ% zT=RjfrTA{R_BWVUf}O+pU;-Y{ffD!h>+o;Nlbp>rMZJ!*fD!87^C6g` z!8sPLKFu=WoT6sp9_?-YUI)rD$Ys5%24_Ir zB%waZ0PfjU@uXbXF*I4M~JNIp#A1Amr)PT98PzZ(u^j(x3gEpJaPOyzQn3 zJuTvQn^RkUes2QG2Rik3Wf`yZ+a{KM%3}JPglmVhQRJdVVD9r}x3quQX~{U;A~Ymu za6}(0!{~V)dx>e4=-7MMM9^=D+nd3WN@T=yDZ$E*i)y~bpFa+v9#aAJh*)zwLe5{64M|n7bNEm|q`3Jyu#A(D&7R{kYlf-Vz?o@Q4V9LVSA z`=K|ikOg%GTBS+VjqArp#I#prHP|)YfUJYWeCPOZ`0X%G=ECN3^pZgAXI9#rx{90v z+g#tADsNia)30(dpWh(I7ORqb%^sdLyGaK4^*%-nJQ5eCa+iblA5UYRxbHPUYTPU` znM2k%GCZ_(8`>k}0KcS`dmgRzbzCU1=;SsGVIr@moFwM+OE=uaQK7Eb0Ap3}Zb(-; zg#@fy;53H;wvc0uF5}PzO%gHgqqx~zXm*dtRW3;OD|syfH5V>+`W;Y%?LD7^o`mHi z7P`?nW2n|!Rn>ZcLj~@S%`5Zc2Jt&n!HgULjL+e>K6A1J-VeT`+5u{Kh-PQy`f6L1 z27tdzjjcHdC*zh1l3N(_J>txJt_G8H7hO3kZSiN=rORB}ttKtro8P?y2|ckId)~xn zR=ZNwn^HqymUos|XcWFJes>Bw5vZzbx}~15d-iePgPysL%NxN!=DTJ!d`h{}4b1G~_aoG7O z`|S-b4Gyh=A}YG&v5k)kVJnkj1JMmmwP6@mYiGD zt>N?CKI#ul<(NJ8zX3;Y9o*%5a(Jo9F#SV8+!y*ukmT*H&yi7&)lXJ3$qlz8%x&{G zaS1WKQp>5^_`xXKXTN82Ej{9CHoZQgKP7nlK@`=SSgvE!Y)}MsYBE^4twTFp#R=+5 zqXjA**`h$Ivf*hQ#N@Qm$0+}%17M%FFL43ldOHqk1_;05vz(P!TRqmT`vua1#}Rl1 zd`z)+CcuiRu<=#mLaUEZgPU%{RPN&a(4>4vx3!_^%(+AX%e0;NxcC@7LMA>e=0ajr zI%56fBtTw}DL)}G`*&ul*Q85DC@<(3n50CZ1zRTStivqPUzO4PBoS}P9=sK}4ftRZ z;oqPO9aIdSjtZVHidQvmSQkDJzm?09nwq8^1zjdk&2*Y|0V8_kd&?#!`6;KjO%C7- z-hZ-A+uqF7qh;M1xsJ9IVR`m|^rXtNFvZI^!)+6_IkdSpmnk;+hP>t#@7&}a0@?V8 zIQI97OZ^^~LA@N*7yohAvslkVtYX1yxMj&U0K;p3HjjM{{5+^=d;O`qhA`gp$&jz; z)GR%b=TVHanLwvQf)(H+6`)CKeE5VjW+NGCx{C1B*e{57fsKdeoZJBkKz84XO9FR~ zQ;jQr$}{*WBuNAml9EMqQm@5K-j}S)2bHk>KNAr-C!NYa^AFx#xgp4$r!nkJwFULF zAFLfmN6!^xXA+`hV7Au|ztnPuKDU2n`ZP)e1a%S;{x|UbDWbWL$V5}x830jX)6h~+ zESi!bvGG|>;}5e6%lYrXZ*$l;>;b}kCYZxKLRVIeCcnS`WsWD?83dPCMJ*8DoCkt! z^|ctq3ceDYtJQK!gO?>DwgQ496gJvkei#Ku4ThBnVR|r z!h9*IIG{`7x;`OWtWz_%bQ#Pdrn>k&Wre!AcSXWH?M~jnoaKbB#eLcaQVF0 z&uhS3yBte_sPs%+9X+ylz5on_Digzv;0J!fC)BPO?_jy;V|zElw{J5HlDC?f6;l$j z9Z8pmA;fpNt6XfxkEi#XMDL@56SkdHV=ytPlGqdnd0mbo)%5R&M+`7x8+ z_zf8u(K~Aj0767lT2IV(JXxRs%+3P&T!Dm^fm#o1P!P;8E~2onMxJpcBu`GtXn~W! z>p%x!e(K#iJ4rFiZWqzBc?Q~1L(r8tJ+$*k79`~+vw3H6OpS;qzPift}7NGt1WlIG#1z;-(MX`ue!_ac5;*_4WQdPxRvsXd`iwJxi%E^|ZDJ?L<6c`*GbdZ1J{p^_lKkD8n7C zWv%JMhtQ3mYK5;UPBv%Hdd3`h`Lg0(i`g_j7$}gYYecHfdL0)6RTR+wP$`cGEeftP zGq-A%%*J=IdtK?1*v6`+IuHyDZ+4HBd(L@ND0x`T8y_TM`=DFF)wbyP65E+~DLI{> z%wW+UM$B)E6PzS4o?g6ohr_N$&N_nVwyzqtco`%miYJC2KnsN}pk@F7q;`Bbu&O;! zCzZ{Ms{mRPQ_zr4u|E!gSnw4IBKEf)5lrN(zb5jxIKf1I?lT)s_Gws|edKOxC7^Ot zp1uEPxLreFsCK|y^EvLvi}vC^AY;Ra9d&o%PUX~NRedJY9!NR{x?Xx?sPwc7)d~{` z^xI39E_t~a0Fs24+g^Kp`uihd;d|5r$iqvg<5ZmKy{o$*YAd?NBbu%o&-vZ5&alH#`hNH&j{m_(FVTnfWMA3NY(`Cp{e82+{IdN$+hW&g+7YHGkIB&?`F zzbuJ4gf5d%J<}d%21XVoQZtO4OI2|d>|ru20YyD_jkYSY-@p8-*~DhO1D0U8Mnd?= z5Qxf@o$F+M&c`@B$9>Q6m^a-g@oqWt45Bez>F6`;rQ_BhI(*P?{hCBDHJT5$aUKJ- zE-*e|1^D1*r8KaHvc-(3sXwnz4pEf~VP&#&IJ@>35uBT{mOQKYmw!=UJ;@I`>iwh` zP6!(WcVPI-eA8;{hZrq-Eo2*RW?AI<3OUxd-@Thc2~XMSf42W*k`*wztiNyxW8>D` zB#%C)p0q&M=^6B)7omlOAYtGZysk2qT}2;6IN@Poip^rpK?n*=Ifpi=TWL(0c=lnF zVft2qe0h43MvorJxn_0GI#|J{PXa_je6|*e$4y!%lH8WN2>k@PcUb0z%uc_3zN+=* z&n&yjbA@xvJTei2>%pRV?b4xZCR3JxR?P}FB#4@Vu9B&B>_kk{(v~>w?=Ct#qoBQ; z@yF;9yH3V4@%EB(`?GVS)^DF%uWnWat4|v;XGSQYHl7wY?2arOfs+1!i!G=j?zeH+ zo7Ph6u7}o5J7m_ zt!0K{ibX~;lDBU+0a@7mB-wNi-pPIbLm$j#6v8JQX-v9_N&}t3L(_ivrhKo{1wr0M z><rdc(S7{hmW!>~I4K$NUVIgO#s5)qt>RT$pGW7gZ9MUeBj%BumO!cXeL-NhQd!}cR4rs zqK50;tO`$?4mlnWJ;W!8!j$A=N=ByRw3EMza6zGTmoD7}DO=v^SbjS7{V~(bzy~qm z4hDqJgeDIPJ8XSlcN0H4!WAgQN3yHcX90@RTU=+pb;mD7y(-^!W*T7^DYpE4koAMi zJ!dFeE>E#=5it7}F^|p@?VS6N^?cro6zGtt@vU!DHZ^1Hz?Pqe`Pd+wNd6L5w^Bh< zW5#P$F|!)H!RVt+h*2_Qc3Q*xmfFk+tpO2vtD6oCM!l=NAxpgkh< z05m|TfG&#B`i>Cb{wDdGT>1ZAq2tQcCqw_QDUGO00`$KA91{5Zm;a^IgK7BxOB=%W zE|!Rh!TjLo^86xe^09g19|38$*@0Wx`ZDU>{!-ZnI6v@2`{o=`7awf);St4+ zaA+&hkpZp+3izCyRO#WuqZGO|IWO17-b4f~G>WPdb&V$}EoGlE1`e|cB|GJazf{Uy zSrKY8i(*9jZ9=W~$|g-JWPxsiKw@7X1<~gBSDmyy3!O}`qer+$cw=o_b#cXne3KjH-#LtV3*@7Yt$lnyj(eTUWI?srvykeDHk;JqrBG2OgXNWI8rJs*@uPOLfq>ZYG&5CwI{+tv>x6cR7ggZN&( z@#0B$fBDjDQh;}p?i{0ud4;SSFK+`HE|he^*?d#1ciP4+rRktfiu~!@)v=c+tv-W> zGJcHJmg%m~q@;Y)S+6tJJ$z5T`kR5j&Hq!R#Z$+e$o|I>Squt{D?LZaFvsPCe;9Z<_mQb`*4IZrC?IlCxx^q&70Mj9h}S-SGj2c6ZH zyk(ogn~x@GbOR;9g<3b;Z7hU$xGTM^_`pzxgdYsus$p#9s+*0Ra%fXeV8yg2M9@@Dqspy9cjmu{I=Tn)Aec*Q44{;K5+-BnBe>OJG{@cE$-5q1T> z_=iCXtd?K?-CJ+TeWPP+e7duh?yf`xV{!W%=y~8g#yfQPS)@+d~EBJH6m0 zAO78h+I-CJ2jM1V(6a$ya-cfIeyP>uB?^g4!~E=gQwHGxw;+lzp;C&yLmEp=T5~6| zwfXSQt6KsU6avAQG=nKJerLJLkUuQXcoE6iRi19IO!Dzs_D-i%Wa#4v_2%__%R|_D zunf*DWh@=WL&KYn^p!TX6zYSVj7~v^RrFGx(A~ zFoi(HC1&QB8_MS%)-iD#4S%+HD!~^#wNm`x-)oId&9Yyd-7Mp2OG(IaWd(0$-rVR5 z#egS{obQ2G;#m3{w%RGXc@a#PyYLOcwY*9x6{%YO8?&0!oMshcvv1gB%5VJUY8?>F zl6XO6C=jgBd->iSS>ZM*)frPdkuUsYp`Bk$tt(0WfxhhdqD4i>;C__l=MsqI+MB$4 z+`ma0+TVNUGAx_XMt{@cW0J{L|VEVq@_c;1Zj}&l#njzMo~HsNJ&Xa zNOvO*(%sz+(tX$Q{q7$hgtPbAd#$-iJL+O{7w!M7arJ3PG^8{NRB$V zUx-lxz1{Sjs0Bp{UL{-g4h#m-1a}uPQ^(SP%}9}@A&dP0ub4;@+xa1AgUo^kB3+WI ze}8i#F(nRm5ydz%2(9yi%jgR&qw2o$PCIH&>b=m0ASnT3e~iGhRUVS84r>0lveFAh zfo6nReQ=e@qJodO3S6;CGFeorJjcH)US>mFck=(+wQiHWX|aqzqaWgD-MYcp6+qgY z{+_*e7pOw-`ULk?r#J*X+Kh}A?Nu0TqFz9|PE-&T5-hVhtLs_fdpVkqRwQ^84~Nkl zt)@nF5U`p^Ffpz>3|j`{h~6R(n)}M)QANEn!+qk`<^pjtxj%m&xlqz_q3!bP#NZ7D zy>cK(tl$VW6AE)OE+DahjN*X?wJ@I!T#Pq;P0Q>TXnI)_r&gh|6;*e3sC~BLQPkvs zYDOf`g`36Sz820sQgCD!H$iJ)%42%hU`$5}>J!J6#cOZS08KyOEGeF01yOL8oM)0?OY9F>b zjZRR1SNo&-r_dbCMAvJ=E%&`(E*oA;G8E7hiRgE7H3@sGny49>2#`@R`sws&XCf;{ z!X~#MEIxh3)P9+_CBjDg$^k=NU`9@21w7eJyG{RxFwhGZNwCuG`Ic)P{H}NGe^Q7_ zl+w-b&>eNch-=GVsa*nSg)Y;}Sv$Fb{c)h%S zMUcSibYr&zI#9!rB~^av;slMr@(0ZFUdSUZMT6sKMHcpQbLK4Od;E||r@%@AO^(@5 z>fyFr``nlPoZ%e7l!uRRPn>nFml*T=X1DhPRynox1UbO|6>8 z6T=X9bDm=bi+vM);AT1-6YOZ;p;`QPR9HvFQG^M{Q~%FTJx_E&u!R)}5g0(>hF{^o z9NtVw!o}cFpa~4ATD&{8$!Pqi-^GCz5tFG9JlP-Y05S^1e~&0tDw4T`3@%*>x6;{=PMU>2rjIX620l^JNI@!5rchYAmR)mv%`7oiH2_>-U2%N z$}T;-ZZBz>(%{q3I$7!=vGM46DXx;G>g+hOYp`z`BvuD)!1{`(ZPDrFm+E-=|j?6+kuUb)R_=#(Va2z6>m`t{dH7ooST1vJSEyt^x>NprA?+ z3|(x%3?xv#-1L^YxPua^o2tN%srTz_#T_Hx7I!7Q>}#fsV={8K&4cd;Xmk=@%BJc_ zFUiKD>X=t>Bym7+bNuel9>Bt`iDN@ikFt_#n!2Z$|)r1u)Wd-&uym4W7o4)aBNM603>LXHYKkmJi-^T z8l8Ft7shHfImT>>4_>gA=B2P84oZM%eHH`CK9x#=Z0x{nvRDXM_R6i*Q01fxXroUu z6&hrJ;Y-ot4y3xG1=2e4DnCUArv`)GKR~fWd@?nlVPqPTLlIG+frt1SJ%0N3ZoV2c z00lc862~-UvYaStNhbAsCJlXK?yL&^%O9N*VGQ&{wS9J2saXD$uYVL1;$w7Ch*7{x zW0xF|7Ey>w{@d&j^ zTaQPy8K@$FeydEpFgF@Wy8qjK_Ip-g`WF@0siAO;>CEyn!^ka*v5T>|uy{Z6n;7)h z@YTUr!oEtWG2Om1vi!GFD&=l1Pm;=bOG87$XnVQuHb{c|Ll5p(3KBbE=W&sko5+Nq z*MQ~hhn)w9$_y!tW;G*}&4w(<2v}va?Q&LZyxyVy7vG5p?CBs3Z}@5gp5w?OBzmqO zMKMFw0=P&*M6kk=i1U(SOdx`@zjv^u?EM!c7+kY0;#SRsECw;Xv(PH~%1_?P@2UD$ zcv^+XRxqHAhKWM6-uM6Fs*1`|_)F?mx4;U7GVNnsYOnK5HF~x}T}~uyr0KwQk_>0r zz;kKGmH8XpVCV<~brZyvwCDvj3-Bp*a8xu6PYy+-A0hFHrZYqYVeNKvuTE%&XW&(F8QYcvEt{_m0CVi3xoZY>jYHAT_Db(uKa0B~jzCbTS_i0t$#{}{ea^H=V zVYd+bRS0}Wxo?C2FeqQJbL}(-FG|=HeDb|n;tT1mr~hrOpR-vTHq>+*d&Juea}8h% zjbU{WR>pd8Dd|GM71Yoxq8=JxZ(eQx_ei}CK*u;9OylVWXUfHX*x=_Q36%pAi7|9~ zWMy;`>fgt`&Sp?BBgO(^RQc3TQHA~)JXG&7x?k&?eN6SuxicNnAh~xc-vWs21Zc~p z(5xa%M?Ap3VD_zcp3?sM?^N_2D>6Uf#4cvK5|VFL@?giSw1DwYB>GcQ0>3#dg%oIr zWd7d1iy>WbN&t5ch^A(F-uGlXB@jUcyl|T5cB_iFKwmlTl%W&44^TC)Xurm ztXRy`Jyi!4>fDdk7hdZ7Q?mQ)DCHzRJgPt?PaQ3VY0UFd?rx}y*tBu6p{Uiae)YVT$mfh2Ugj?-7K<6Eh{8V!cV`jMcw9CAGGUQex!<*{wnP_ zA623jpyGs42}_#!`#D(=MR%D~Vdf~< zUff`)a%3h36EhLoZe`So0~oRv!`B`CHL<0B9(MIsHP_vph=%0r>gpg`13~*sX%UD{7^~kCyW6nITnku!+OwRei^`8*gPTw6Y;r|sF*w#mT+uEChqw0B zblg2GIYbF#P~87*UcN))Mo+{EQ#3a$6T#j^>&f}Z4l%>ru00$Rfa8R*u|;k0KSe(p zmJ{#ixO!WqBb-h!T_;xG17_Q7ylD2>`q;48P``Q=-+$O0+5k42%+dn znX=yrBEGH5F>~@0^EZbTDRYe-=I$N3H5fswEiw70?y*BlC2#p1rb5?tumOMlCN=?Z zn`C#vbKPEBEU9qXID)7liOb1{)mtNdeTc%ozmN zk&AU+<>ourPb+uily|M)Nk;0xYRNI3t*9`>!ii0|-H`|P=kA^*c7ImuQJ>!uenQus zIJJ}di&iY~{&x<&5GzE`QTKtrmluSMqpP~oZ7FVJ6j8G4D`Py*d-*>ShtXJ(A?(U@ z{n9g830h`9|1F^2n&})GD^oEZ_%3b<-O>koRB~moofK22cn~B%_uDl|sJ6&`JZYQ0 z`pX{*2ZLWp5EzQ36Xa9VFrH5LK z$~SCZX+`2yaL?ZNRIfHBDJBRyqU_(a;)y1_3eD-|oD$}qEiH$-pD9xrz7g6K$S@|< z*}~+ai4e`TmH}cm7F0Lg=<@e+-&7q}XI>)=$#B99$)FevN-u}o&sW#@?k;S1JIL=cmUH6_s^tpBU5a|v`*sUxkHY625fi3NBY5|7v{3kXB0q(K=MnW* z+{er(MW(}nQGU}+9~ud){HI|R(7AGThDcjX^SuNGq|_oJjiyu*?d!_GYTf1Sf1`6 zg&6Z>P|yn+%y8}N0oMZ_hMlo;2b~GV(c~thSBd|))&s-_uw4)ckO~{$I3r!;(U0WO zdFCJ9#C`~&TmGfX0Q{bBm!=W!;1x(yq<%}pKI7wlWS9K>Au_bv^;khX+Z&->Js>N* zWI^ZvP+M$$c8ld3Z(VOz@RA170n?83p=#OWGBKnue$-{cK*n+s6)T>Jin$!Nlbs}$ zNT*lW7oVSAalJEEgRjD|U@B7;IkYAg&n@1-zGJy+;c?hRV!A|S!^6*s(XZiWbPv@P zTF`li>#A)Yy;GvJUsSM-KnDt zvvhk}B&UeGA2v55<2T$&dJqGv3$hi^Bvefu;p7?id=(7qL}=`yea_)RaBELX$hgf^ zqG^>nFSo8jLTSR6#6~ z#xjPl@e6^fQ2pjQ50(DsGk$~e>+dVP(nrs?J8I(b+|{4a!3=gaW=gCZ{LF>Nv=E3x!iYkFz|7ZHf*WK}e! z>e1;ciGJ=ds2`3!(`W<})p(8GNR1gY*W1yIT>-T~junr?B6>&24>7CO#>B(_OzLe2 zsX5ajqTTC%<;jX*x7M=Yx1K;VBX8{)+mu5Nz*U) z-}MnVo+oY^4%_fwibeN~yB)sWt68M=8hW$y*d=Jm=_8YTS1{3n7DQLv6~J2i@@AQ( zE8VDC9&s{RX==uDkwzJIuy^_;NE9yA+u)%;mLV|(M}OX6->O-V*boHISf_!_Af-~N zigzIFSJ!)*CXMk!t#V4_%9;75s%Jebe^j-IGRJN#_Dn>k6nbjIQ_+6+<=O>l0@YgQ zJWPx_S5YJ(n{y_3Wg?dN?7GIU$0SasxH~e4mb2Y7h1|^=K$=inX&|heI?Q!;i&PU$ z1;3VWY0_}uUe{HM29axPt=-B?mMvxSR35X9v2!%~WqN zOhb;01donsZW7`3HG3XqcexcRa1>x#eR+R+J$Gm;%O)9d*wpXjwc9yckDYetjxP3E z=}8cFG(SYhAa}l~G!-Hr7V#24`!4=5axZhV?6cpo7JKrD#i(sGw-tkjr9h6E{eDi*E$TV0b74?we0a5>;p+3G@OFvFw+KsR zXKjp!O4R0Zg}o|6hGqLQvMqGee{FbFDY1^hEpGMg`|ztK`TmBFadHz{lnW=D zE*ZlbW<+5$H%;DYmX%qYZ%kXl~%`!A{`5)+*#GudqfsP;zDK(%=s%H6Ht|Hg(oW zmjpJ10*MYsSIGm;F2j>_D#cuWw zK;PFtWTls0QB?gi{OiuP{?2qxis{c_I(hQHc)2fx@hFLIxmxhTWZEkwBc5jJ--Lq79DEP zE-o*QqtrKpYCg+?Tqvx#v^kpW*e5{bMXSW1;bPO}_)*tKZKrve)=quhtq@0=D(j$5 z%|R@kY(~=Uaft6dZbfwZo4qLmWj^2LPg(E#4zrumv6qXmN}g8mfFs=ysCH6hmK3TYaZ=7$bm|ZNie?9bz0>`OO@6pVRYiL1Y|}=Q=tia2xxJ@& z^N(FzZ;!67lS&`8)fFOeVtC(H?jh z;XZVY3PRrwwhM|9Z}*Cx^`Ro$gPZet&MPC$_&M)oGIMYoC1Yh0<46^mkA!iXxBY_} zYA;lTOEoh{U|^)c9=pk5cPW= zUuyRvPUqoJ9rD2ptQ_^3e@E+ZGkR{4fv)xCUJxsnA*+*+=%$cgOUZJsKYfjT)E35a zaF|xDgxLinrUD(vXeP~H(xKjOPm4#RZMdG)o(s!r&pe>(KP<#qic0#{D0<{dRFOK1 zf+X^$^D>_9Yq1`y;cThsAwz$+!|m~MySd;Q9y{|laa68%SK>WZY#z4r*K4mylPXun zS_NBq@v6PtuRM-wtSD~pxt7h>OrfQXXgiw>5A>eIB2{18A0dh|^e47IW-T4^Cg#tSG6^E;8G2Lb8F?=| z@aAd=)=Dch&ymm9GzoOD4XbE=MBDeYbKl&$nHb(fOczgkY!)(dmi)ZoNZDip`<}C4 zD&>@>NtLRdiPp>3=Cv&)kI+@ac8^9$NQ?Y7Wu^vKM!Gb6GUjwXIn-rhAt#H9@5nfB zIAH&2Xczqyy)(!C=>>8TRI+Wq2#`}aL?N*y?qga)&A3HadqJeo9%DC`u zjn8>f@aRNsmsTD5D3M@`rYepZ_njJ7UHGsH{t8CQumzaSlVtr4%hx6HP4tak?Gsdf ziqi8{q~agrEOQ$;OTrsR(lhiNF6AO=@Hr{$SwCv>vGrbm+bXQ2Gczu9eC?#tGo5i! zoN=~0^E9bnSK%Dfas`7v_yE73{yn1J&Y@j;>-#lx&Bp>8g?M$3F1Us2W44Sq8LbKl z>CEXS9pF@ks0z1$!U~7S9YrDUM4S1|h;&np^mNHHj)6PgjY)@{2=6GnbADte{!w#D zsGE>iYs7eJ`e4^@bNs8OYoa)?tP`c{QYBLoXObosDBO$H1FD|t7QdE=o19}mO}k%{ zzfy*eXSwYUvc5~i8n7Oup4KZp#FWE#4Eh4II8v3c&E_nk{TeYgc$gZ@iahgp?3*yF z`*6%n6^x1iG( zD;_!r-iO`t40~e7Q>q!l!^F=SP!W>kAyXXYYbARP0Jb^Vx16PM=HY&9>0ma$kzM zJl&^pUZ`kZPP{gwA=9*Tc$nzux*T;|GcC01+vk1jR*yn9etUZ(u*KVQP4~m;t$Bs+ z$U&R2h_F#3K~F(VntplB%!dLAFV4d0L-J?}L3aeETD(QPiVp_Eu4rnNob1l07=F>w z`9drPEyRo7Xx(eek8czeSz##Y=?VHDwZG)iDYi9J_x#>HFFxpmWYdo{F0&8ZZ!qPg zD>b_K^prFwJ+@iwEIJTI;hqs)XDuuUp^`lEk$c%v#iHL|35tN*g!>sp9W^7E?mDq$ zfKf|eSg#7l-lAWnwU~_(G-G|UxeHDJfGI5WCihOb)G zn$C=(?VNAD2(cOap>M(zd6dzN22vkJx7Z4F=ij%z?J~f?7Cla`qg8t=84gpi)VVKkLJ~??e_NrErfQrZf{QR z!8+&IF6PcHIURiIx_lWltDLJQIC<&+wC#z{05QC1uK8s>kJ##Cv@x@??_H4oba`Lu z`1Yza;f8~qRsNV4u_SYN?9bG4$JoB=H`EgMc zgj_OEGJi;JE~)B;9>x)iOOfB?cXAtggYyk(hu@nW!~Bq?fux(j;cYTOzp;J(*87G5 z8|>Yzoae!({iXd)>CWdf$Uc9Q9WKy(A4Dp9V@a<*8Z)z)`p&H z>+>e^Y5!|R(%od4*($Q&?WdsU%MYj)nxknIVa^W&(?aB0W<>jsN_PURTDriALY-@9 zlIZFi#9Gat%Y9zxw)<&lJdg5NfisF(JK)g4KVsmEv<3w|f9iFG+%NGSsh`Jc24R=J zXC;Prjru@Gp5OY%kSeBXHxNq`uyg^-jHQvyxOcuESTihCV%Q;#IdYKc{YSr7$E_g- z;)3+^g(Uf+=gqT5blpDD#IPL!ZsD3)AsUkX^TW_<^Ylb3{cjbXh>wJ~GMSquAKwwb z|Gno~*w2z}tsxB@^Ri=ky9Cc(Rss+;Xr|4Mo403c3vPMF^IUkT_4w(j>@0h=`e{XZ zO2#_g*Wx*vnH5BxUB=PBpUi(!JFL9v*n_XX^0{|SUZWlRGjhbcUYUC)T;qFxr*yq@ z+I~!+wD^$%zr-kz)O(bejU>*^c&wI-&ExD~ryq zvk~|=^x1>gubSgo@88=ka)13P88K3!!d=wSbFyE2b=%aD#7lr9KRxV&md$x(>hTwS z;rbAfUHa?Y`8BX6`M_fI)Ik05!cO%cY0KpVzbHHk6?`SsizA@EStjC?@y=)0{JHw( z<+U@7G2SyruR*U~MJ-58Do)<1#s$xH^ONJjgDpD1KFBQAkbv@3B-^hlMpgGMCyo9x zbw<;tm-jP;h$1zU-56SnpZFsMO%>ON8VVqrN;-x=UA*Ute;sluc^o?I+Z?awxiyDR zkp7u*$E^HSD>)Q8UKnl1b4#9Kg8q#u@rOal<4Q}R9k;i;mfgHiuHyq$vtjF6_kGK7 zi<)mytqsm{5o9ldRx+U(Ux0qBl8$?S?YmUEd&~W1#w1^Y{-!A)Gzcm$JUY)aVaW))XtV}Q{yZ>!FUDkZBss4Px%1gNB-5n;t zs9by}J<$M8D8O8h1kpX9#bM0vxp_X}4Rd9K#FNNvD`yW5!=1b+>!JonDt2e@nn)YTgoLAU(Mp z7aGCblBAmiS#9yX+3&CK5+;D8Lv<=F(H*+U!`FiA@(&n3w#AVda~Iu1S0Zn3m!5x+ zW0uq`4WR2l^cL>H#tQ|J_~ne7Q=9e;>PK2HJRW?=&Oo4)YEUa%{jj$!M4(sf`^yXKZzP+-83&mDxwm*E zK-1mnX?y3nh;m<3?wDyLhI!JE_~8c(`+f0tOhVf3G+!v}=J#KEH;$U0yP>YU!)!(J zurHpw{l{U~`q+DlkU!6DXt3hN{%n!S%r>o$wKCP%1a@C7d?pqyh02b|AZSqj<@5hz z0UGijV;*PD3cT8yqsG&<7Gb>y-{Qy-*7E@Ce&W3S2w+B^cyP$Yz6c^x7MfS9UJ(FC zy-iSar7>vQs)EHGnj!8Bd6_?&?Ob3Xoh(;gd|M&bb^Hd;w&`$--k^M;tX<<>^W`Lf z{_Nb!&c;4>yn38eX%J1G_`)g)R*+M zovg*aGMph?qtWn_n^^_oXfSh21Vg;cOPKX%IFni!Cr|U?@XQ%!7cywl#YEk`(zY8X zR1^KMgXUuCgNMy3&!ZuqA7XVC-Epns;OR4vT!o98%89@H&WB8c$)hAxDu*CjY9Ua= zW(v>Supj?j_`63TmnU`~p?#7K{Y&r8xcqz>=r2mQ^(V8C0gLmiuymZiYh8C#4qNzH8& z*LOouLN4=bSNAdaMP@F^+qtaN@@d1r)}u&NJvFJJdc;}r<|s!a&p`=Qw2|yV^FBnX z10i7_TTn9L1ufg#K;29`7)K+O)`9u@$F%}hHBW(7Q?`SGbQ`v>w4DTu-wb}ayH5rj zCnH8tif;O3ntC<*(UP56Jl<+u%)P;M1lcGQGV6y4%gL5Yf9~n+ZQ8L)y*Z=k{nCax zQ$0|)86HYk*|;`Xyj_VJ+N->XRU~_RY$0v>Bwt7od zGXoDDv6>O%144M3{-h520aNe<$2xP0*S(N}P3sh@goRv1? zQOsMLszkT(ov%|(et(-jaah_y9V8m4rSLn=d+Nt<)@)&;D*Sh9;n#`SU$x}a?VQcb z@owGKBMYBJ=g~A`BK(P$A7h=qtk|?G?k=F0a~n=I*hpF`F|z7z%6poYd?&T!xqA!{ zaLSpUro;4%C_L}}3Xy#24f(mC=Q6u5*?M0|9R6gWwv$AOl;rlr=VDvXH(Q6axJCr zsToOZhRV-}{22wnC}@6BeRl%=^c@Mlm4nMMWwc(K(M2ai^h55k@TZC5+`E&XT6vl! zT?O$rpIol1J9i-qE8+VGjPtko}ccjCxs)wS8Ryf&i`f${K7ry0TOL5hgvNMuH z%h=s*Bw${xeKI>ETX)gb<9IzF8y($WzP(!0XUcAKKyz-DXNZFd^EVCMr_zZ7|GpV^ z9{re2<^D^J;$v)P_Ow;2Hxt{ z(21RocHa%c;dR%X9XQ2#|Aijo7c->Ry&e{Em zPeF9{NMbcFBh-j44QH*dTlmcX0_9;Il7RlZ)&WnxKN+&wq4POD!-*VvxND3Tsq8@A z17LVRCwWzCSa7A^x&P`3m}zr-plN)b2QXZbPsa0AiB zFn{D9JX*L!Ur~e|Ydu9x6h%WL)5z3-cqJpSF^$&h%RPT%GUNT?9%CiyUP^ z%f``HBwrgXOSSDdD_IVcN?>VsJY6<3&yY z(4(GKTT$c}di(^Fjjn&M?fhMe_mE2UN4L|q^!e+xXD-|poJ}hC_(!X4zVS)LO1{C$&}ZCCj2XM%`p2{#P@a&C(VVb>%N?Kc-t4)jORzdOSKji;6jC zOu~^>F~;)nyvFg^Y3?JRPjt|U4$Wx&!h1x4IR2rv)kd=@;5Z{WNYIGvm>cW0>8Sg) z>Aa}NRc793){uV+4fCx9H+eco7u!=Xp59a2<=eZmjm>COqn(sc8Y)gOR5avuOoJV- zS>CT+g=g|$-gKd+)KcK93Xqui%px*nO0nN_xBXn_2e3Qpi4kuwM|KAZ$^gR5_lRwK zBRODBmC;wA(pgA!q<`>JKBwBcoCe0{1OIdgJnz5P(1+@bdTA8B=b)*R%pHHvv6F4%N2{KN@J%D+JYX zXxp3~09Z_VDcLN#>*Sp|*DdqC>P305-#zW`QAj%c-QWYuVQG(Dn%ByE z7B$uB+9C#lz*3c_Ftg_k^#a$S|BSc(gRcolknUU@ysay>@Q(lzX)7ALQA;x>3F>l$ zr&DLwH(k3(wO&gKzmWo-4|4mIme?;K0!*jM7pPQttXpp(^zN8u7BDi+NR%@^nk(8 z>Xro$uRr($!J1l)w-6c{Yh1Xj6=iign2I@E%VA z{{_`A4XHSYyU9vx>zde|%EILpRh5VB*J&c_O}l*(+E&Qi>l?etn)eNU^UIHLAZ?M= zV|F0=72wOBif^Z^$|z}I`N4ZPpC!~J#TM) zZ^6s7O(D;u?(9rsehoDKw%fn%b~davWK(*qL-%`$RCkj&8~C`;upxMEbb)iUP>Dc2 zeq}t8@F9P!z@Xfqn3zMenq`;im}^Zpk|a`0FCi0TWC9ZcxhZ_vj%KPl*FA9TG@%CH zz`O}nFR;#Jorz5vpZQfq(=f^1&AExYH)_H@eWO0#>W9mCdH|>+Ec56k6#r1KVxmC0 zrQq{i#dy!iO^DYU!B4lbsrO^0WjD#y6NQNHU)s{>EAxR*#xo~Nq@-!hxIHbpZo9*$ zrHShkwM>qC^LTvso5$C#e}XONe`>{ZK&q z$-^EO&gE&1%*F5N0|0v?#Lc4GD7+q6HP+_3-Iec09CxEHxTILFdTD?2pjXs%g}jlM zDh={m0HVRGE9675_WiZ8ufFG5kLyX|Ty-VW*)Ciw9Sy{Gejk!XIP z*_MWd93mAr<%?UiJdtu!?t&(5Rx6)UzP>Dt&=R&77?^hCVZn<&Z?5b`{hm^~8vuZ2 ztGT73=ShyLXCs4O*o~D>8*As7HLFUd36~W-;C<7*344apUO5fTR;g!XXgMe>jK+tc zh8-IsypKH7w@r>pHmvJxvLq$oj<8{#mB3D)zs=1X+s;>WwovzbzV4+CON5EY<`*5Y6=<$WA3 zkK7UR4J(P2IT?2y39mA^1LfcMrQ3XCT;+d7DBS)@j?^JlF!Pk((R_(4(14-J5RGM za{{aI=&-i)VEVORd4^IZ3a6E5h;A)d!)U(w5**!9QrUXHF3INsa$asCF+B3)J#y?Qks1oB&pYXI>FSbeMGL2}2tLHA`019?^Wjg9acNoT?)@V39 z%X8R)JuOoo3sAWQ^H<*rFp6*11$M+PM{$zoYZ8@25kVNPDGn<+xqI<~kt< zimGK9N^F-+ASvjZj)to+?J)m(xbKGP;jUHdIr)nlDS-Mg=QyW3Se`7#AV2iZn|4kB z)>~v)IKn_$*uj1@d$3yjYKYgvYeZ&p(;nlz{<(uNdy9JY=Lz!l*C)q3l?>L`2Tgka z03xtZfQT8@E6ex|x_55%C&!hg@UPR~+*qvj`(?<+yVDVg*6gX+ii-imYrl}gDY{%C zhVhLBYyE?in&-*1>qrO4a~-*E%m$`I#uhHQC%KO{gVhb3Uu3?9s(8Lo@>D%Z{h8kdM>CPy*3`i zjy{AKuX>pLUYxma!?<=@P!|&R!0kpQ45}sfK_OX6`H~nUj2fOuTbcW^;2_BwE>mDC zq!$VUh9OC;fiM0xDCpDjO7Ye!J=5j`bp1G<=JX)WYc9#R5Yi!eWO$>}9+0}}IErpz z96vJ$=^WjnJq)ag5-xHt%rztarvet3!J z!bsR12)xye;vTx8{0Hk}6g&L&>Ud@TmaM#P+Mp~i zp%|eXLbVS@!SIzm?IJ*l%w&fBN?vX-i zK6NTL)t(5NZEwfx`Dk(4zhhc$*B(FE4LuJv9mo9IK|f^GxRez!knrSok*~wgXK@7B z7qHS`sJ8$rN({)muYk&}r||ZOQh2%T$d^rgHBT%%&DTU9=?h(~gF~z}Fb!Wl3!r?% zo*V<1KVw&9oi~Txj`rhjW(Q~bahVgtWae*+4HuQ0r3q%%Ojm~nzfe=ZnC(%dy2%&> z2}}nuaS2KNY%fdURh3Te!r&0b_?4RZXF6hrPAHt0WSy7pBGBXS7s-$L$BQhYushN~ z8K}X|*ZJW)x|6E|f22TF)r?#q=zQ_+BY@%YRXY_=8Sf=vm1fjl&EfP_?m9>yGk`aYCL$B2p2x`$T#mN`%hy>?p+!)v+SG4Wft6H)rE0iUDaW>xx-58Mf zcW;bn9D@VUqK@yQN~Xg-MmA&!@G>BlcI=U2lV`<+H`25Zz5jN$NraE3qD%y-cwrox z?l1@7cuK#`(^Ph$Kn5rt!+ba+Aq}~4JahJJUlLH9$dg2n{g0RS#&4j{$$KKQ;B0F2 zRqsJ>n{ftP)>xB-QNqUkc+E{d286g|*pg}^tQ_b+C1O>!1A@MJq}fp`KE_vAHBQe_ z_av5>V~2+1qhjmwXuPA^q|ajy&3QZOYHfXc|Hz7Dr(O;jy>@H2A^aG3MW)L3r+gb) za~Qo{6+G#xfbX^7RE(c8Rahb4&ryS&t|5X2z*)>53DuE)A;(VVFIn#|9@hT|l2T>; z7&{7lQnQ;s+pXOs25NYJSVXgOGPqLwOP6i;#2tALTb%hyp#Yk%mjX0pHJJVxf!e-1 zDY?}6CgI{k`syZQ|4@NI&IO?8CbPs)<8K;Xt?;`*jfU>f0+nVJx_3nu8WJR=4L8 zg}|ag!HyxI+!nxq@S#nQv26c=ZN%Dk)nqP=*>qCo!eJDwtOvlE1Q2f3Ys&u>)ms_r zXS+)36%@n8(y07i!vSRlzDPwVHC}ZvSLrMxugx0fPCM;hU^^N7(?#obztEasd}P%B z>BYr20Un#c1Xq$wDU%QCAPKsM9IuiqZjyN`H|tsNu^Frb@NH#1A~;|q`gJR#KYpd; znGV7KBY*)P1O`q83gLjC&s9_YBjAI|bmR*=-Eu1`0aEVw8@NT@))hvz2=r7alEw5w zg>yBhT~^SIqN@=jLlR)9vaSRqQ@WRQ=%vxb;f)O!Z5s}v?X7q233bt=-sDgdx~Vq> zTmzKwH{uG+srI}>Ik_H(+dpg4z$m>^$jX3XmkLyA9Dq4P$Dij3`8vh&j|9-G ztScly8qjwpo%Vcrfa%Jkm)GlNz`0xz8?RJ0vLVUmI9uk{mSf55EhElnG8?31~0N)+;2Ux zF#-SzACVSF4b!-p`vuD0l(YRcu8aWd&H(UF05rE=es~|%4E1uZlJY&W2E+c4&0CRm z(_+<>aXRQx6zWbp1b{_asfH-60cT+5TNTOvm3_w^SxQ*3_r^OAIf(c=VbAeS^$b+U zhLr_ck+sv(#ff0+N)Y@H`3|kM*T2|7ryAuWdDl8)dA!Xpmy~TVxRx1dK`nL<}2b@9oDT^mH!XKw&)*)z(g_$@NyBsOCfFmgM~<=zk;C@qct1z@R_36cRt z91G~KN<2hPjWh%JYa*Eod$Mw?*eMmj!Nh_OGTfs! zU|j$NJWtMWDv-s@HnDA*%Dgs6(h%*RW93AqlZk#I!W?2*A3qXKY%sYKN}erA!1q*rHOV@tmdln=yM^2O7BU~e*I z4hQ%!Sf%&Y%VW<$jqV@Okm_#=$hqhH%3~CFDARsd2q_B$7KcdvD~tZLpz22+BrDk! z!q+q{CJ3e#+R6?qEdLR`Hh5hfT8Ze$rlRZh)ji;Ttee5?ext;VAWP%Ajq)(lSD+%o zG2(u1&FND%^%A;LH}5(G?`+7#%O&EFu>=-e!_QgTiOoZ3&>Xg8nMl36JGBNs6}ldV zfHda+)2E5ADI>J|@a)GCd?LVODib}*_z`6i0 zq*B1`?MP_*kwZ6Fg$jI|8KJu)^)XdPJDPhOGZL%>q&8Sxgl{S$vNZj3b@~Z|0$|KT zdAnHgNqx3bDR~rt(5M<38e;ZYTb1^^M5hFy4bPZlYP`gz!!x@No21BxVLt8 zct*e|2yry#8JirA8U+-<(6YcbbK_Mz#sTwkss(F6Y~oyvrAB!j#j(E5Wa$XSCfm2B z{Ex?Fae=TDF%#qp$b18UkfGy|!b+`Ah|{fEMl0xm^NL-8F3#BEVATH_9Sg3+_fQ83 zfoV$`hgv zJmUY6FvDCYO{p;f7!X|e&c%Z!u@1`?pDC*&Qs@0V&e4k0XUeeDNL=RYBTSk(rQ}Zl zYVe;h<}enTb5~_>ia}|ZB8&)hM{jT|fRJk@6)0u5)4}j(R9{P;!fDAh!AuaLSyd26v4WYwPprey1uvMVLcf{cF;r^nK zcx43E9URaxJTcsl0JTyD2(lo%`JNlmLG?4ri<$-*>pIfVKVhZ}*MJUf2C^ByCx?X# z{-bdwLeZ*X8t?TYVV<3imN!|UJOeVPk2xjz_iJEn0kigdLB3P6mq^v##>`;ZfCgk$ zKlUS=Yi8HnXa5sv$(bqcLYnvyj$=G#u=YB_Zj~B407Zc^B#&#|>TzBrLZhz7%866b~5^;0iZ)l!+^a%X~ke z?1)mly!VnOOOE#Zdg~$PZUwNnByK6e9r?H4e$^_;cV+lq_MHV@ul;OD&;tpUQKILo zEg3T%LOxBB2de!5CtNwPut4_3b?|>@UwmY8I9d`cu@za+&-NmoPH8Eilx_k)UH4kf zFl!b+AN|(X=_RW!q_Sb}-xhTYD^~Kbft)|l68Z0lsURU&H^?k~oo~k^z#B}K4WgrD zWh7p$VFwq&gN72bbOj~`IU~OTTbxUWW#{2+JEXu!`oFh^>zpX<*u2mJdN5lY*M^P= zLWv|35-JH7CljbWpg^ah67ZWwHGjq78WU5enDdtP0Uf}tb%X!W-kF(SlKUq51@~IV zSEEb1w-;&BbauAUoUGTXBq`8=x_hmE%y49#Jd^i9Tm3enlVRZT@|qy=KJ!KTjgir zT>~_#NPwICRo!|Sf%oBrL*YJ925f+z8E49B4PYGHm3cHanSyPz@DXU3X5l|;4d$9?>Sx(`>-BO z^h_rJ$AU(G61VoD83|d7`~Q1qfSTZX%|$qniYV}8EOC8buk#6^lEmy}9^48dNFW1P z6l5JbGS6H-^77J=1|S-#HIOa~Bg0^7R1(|k)gld&*XjKeXqn*eQ4KF@pDGDKMaP;< zPNqz!C?8kr83@ua;rz_ln8es$(s821uQC3%2C7R@rVi|wt4oB^>tw$E2ox;z48A-| zG6|g|mM6TfUn>0gq;ntK0U=1+_la*KL;in{tPRjhE-4rId*_D*t_&~bHO)3QwgDd- zQIg|a`pStO36`pZ3KNeU9S6thJLChAy)0~SX*m+?dwj{ErL*VozxzP!`{dS``n_9C zGf0UhS>i1Bvq7^es=`3*t>k0<|Fw7J|4?uLf4bck-6Dm_5|L2JmSwVxTM-J~k%_rX zWS=r7vNQOkMI#htNhl*^#yT0o_nl7qt*sc9p>W`;Rwl5DqgPLT`8|G1=>TBQ_*w$Y z^w;hLz{jHWpp3C4+kC+Cy$hOKc@X7u_qbKcG58DlWTU9e{gB+mV}}sGR1bvruJTs- zNA5oP_>}%jRnzqJ)R`k`{b5h9_4^S7PWhRin8=WC2E;@lV^Rese(lJndA!RT%U0Ta zfB?aBI%8LzV*H6@M{THx9QpO(LzI-={R7~bWL>Yi>fk?d(f>7(-^g2UY2yo9&2JvL zK+Kgkp9si14^7MChn2Hxy~|xet-R-^K9zkDPSeFWu2KANyAuD{vv|fP>Or^Mo#R&j z=z{;FYz1#z$eJ_j{Fh^8fdVL({#hn65czja@-M%$x(9d?0(xG|-TPCUdx%hI{y^Zi zwxk^Sw(?u-UnOMw+%%VQJF7ZyTG@^ARYQH(FJCiL%jR8>PxdPN)mD;qLgmbELP(`= z2mRl3O?${c6||m%JNe+u^btviMAjDD5nH{t5y+2KIUZ|z;E;NmcDBXhr z&Aqn+bRW#_MIQ!iHlF*-n2wacW$%&F-kvbqL;v()BgOc|$nB>8)J-&$FFo*4x6C=J z5Lx3VZumm}Y*DW#B;sMuexrvRR@8#yb&EP`8T?DZYK`M2VEK{TU1DUMNhj8IfTNK` z$fzz|sRIyW#3271e&yi3%0ZvT)84Wh0@#kD_O{E5!(aUJzqbDmp?#2g<43Z(o!Ne-vX2D#BoxlKe>?rL z{FDy3js7!U65RN=OFS{*q7bD-P)jvRHWqg4I0!O#%2$-nyT=0WYn^^GlSGvZx{UVh zg7A2}qU4xO31DR|0We`v(LIo;17&h>1^cPzmyR60%7?{<-;V@=t9PGH3B=panU z-5%(#-e^d9|KxaB%{5WgJEh+Z5?tP#_*T#UEPwB?%ZpvVNq&`!hM)qSPn_KjxwU7$ zL2s_4NVs0GWdGscX|cC=r%Z@HJrj>4vjVQqbxjkcvj$Qz_mYvZ57?S zlFh+|X?5v1_PitapKEK%dNT1UDsG(R=)#k7!}n|(4Y{Mt%c>J*M_)n)y@j7gpcRzx z(z_+1qc>^kbPi_-w4qFgno7heau!l?fSaVCiCa2n7(RYVn51VAzNHv0 z7S34@KODa4u&p+Vvy=%ohRnN2g)Yj4EjffKhHZq@NN#ftH`brr60A)dUxQarqp3$< z_2-;vRw8wt^Njo2?vQ3eiG7E&v>fIw7D@-3Ww?%A?qo89Lf5P3u+rOI;Y-688FdzY zPU7LKCfnz>hF7;wY^?}wEA=ur-X;AG3CYpbkCNGH3n$2I_s=yLZntl%H@*AAG-9*c z5B;%ka{LtO^AfUL_fivkadiNVCBuR?b!l6{WjeLf#-_oX1y}2-nc$k0%`rqJZNB4< z`yzKXe4~&{&)7h1Y{YGA($?p;&yNx*Pi~1prr*@f;g1?}^TVOwPey}K*m@2}D0~e^ z)b*X%KfmOQm)RV#FZ9X@ud+BTmucHBb++kC8_}cyQ_1C#7NeHcTe~_;O@lqy9QoMT zIQF39vW(?Gc#xFIzAd4jDi?T$;nFo+wqXoodwsiM!eLX{a(ltudxcUHGVZ#omdIJI z;kt8{aT{v4wzv#-$o8lM{ogUn)NCaQRVzoBV6>#dM3dIWiF0h?KDwcpF#<4uiFk{~ z92oM*v2RJ_tStdYaigGGvb(sGq`vf-D(EUk1sZK(`ud!NET%DZJ=O3jE__i@CtYTf z=g>Ieux0UPt9zi~*ESwXVJNB_iqkh-L?{V}mh1iX(=NYqr677Az#7!y92!2C4Uh*4 z`vq$@71B2oWa^^_xVNjgoWjQDjE(VkNe7B<0PD?DlIz#a?Oe4%e29bp2sd8BFWD8A z7iu*G2Ovb~Yn!^q&JA@;D#T{nfV8jS`k>f$ zz_7+YW#JO^t?L-s)0jl@htccs;zRSazMC_)%11KGtR}fp3>LH*1cyBgXcXKEUZ(x z?G@Cl@dM`Ob8B+W57kkY><1?E!v?>**VOuyEQXehTca1Sc+Y)5U|Fp}b+kEW-GC}S zE$FB4E}n+UYS~AuB${Qg6hGV2_9Ux0*(TB0+`X%H5qiFn1zJU{m-WeKL%Ui-vkZEk ze_WTHSRnnC=-QHPBa1<9OX0xFLT1}1Uw;4b$dNM94O%Ig0k0-}A_s#Blxbas8&< zmN~oyUt!mMTK5SsbV~EtS|3iT17{Waw)~xMjmy_AR z_O`}pMVCo7sPyvM2WPOiT~SvUe(I@h+U?9&{HCF1teM*t(v^$;FrSYz!b*hUhM6Eg zG(AwiU!RtIj)s&lph)$Vu3hD-Snu!Hx%XNd9buc7fBK@ z-HdAX$z7l|ygijdLbWb3^(&~A{&sfL-$Ju`lAbRWoPP{tgYZAf@ufXu{ygLb8rSxv zv?VdzPNrpK4D`f^glNc|UO6RSjpnIj+)Y(bQGlk7S+=QU&aH>sI?f+S+!`$|A-~Tw z;hTIfwYv1ibY%4*v!wQnj0_V-{^-e4v`o(4f0r;|&{sdYzz%CWQK8Z>#$MGX;ESNL zAC#j51j%_*x^inUJw29w06e9!1Tz=(h(8oKN|^<#0T!8&mTqjc-R6unMoiOVu*Isf zr~7ZLOjg7)eZ`{b-O$zL31isMUeG6$K$~MPPjoA#(Vc_=cYpP}k!(COM&!DzJQ6AD ziXt};HkC{axAK7Q%rb9com-Efzz5F%w^x0_0y<>fhPq|}G{ zYk98!njZf9B7a>!weofTIESS$mHAWH_1Bygqg!uGcP_75z^quj0M9B!B1D9V9ZJ2+ zMKiRxK&{tCIL9NP>%266pitt=nm3E;4ThK*l{;|XcLtyD1f6j(tvIzHYAhW2`2<)5 z9bCWXa0k%SJwDtg!`FIPxJCk2UrkU!n!nRP7$8oxJkHHlVH zKva%SZgo=^zpmZx(2~e#y8x=4L*BfxaMrDnC@_$YbJk8ZMui2IbB`c%)dupL`g}4la{_(Vi&@jIQ7Sly~EP)pSmjD8K0^AdV-e8yi!}FBR$sws< z@I!~sD^P_>pQ1~XWHD;odFaQyyjB#wBNd^XWW)M?g^hYPZ4QPVp*YdXTws#VB;FI{4>< zuC;ZO>roEXDfKS?-Fp(7U8};DgO9#|YeG+JSH5_dmG*M;%b~rIO3hA36R7SA7GoA0 zldZ5|EdkJV#fq`YY1UyUpDVCDb8a*m7KA^7A230U%2|aPMfV^>Wa8nPXEE!gt|-sg z^44q{ciFLK?J!QTRCD&`1JWKKVwP2cVOKKPw#tQJqZyVt-abC!F9=JBQha)hFQ)J^ zFlcqD(d;Y-8dce52%{-Mr~h(VoU&li0~FbJ$K*j|iRja!+13XXHgyA=dRc?V!lbfwK2GOV3z`+vKIfAcG5`(uvpbt>6bc1qV9+OZcWS-+*9s#C8e0J* z)y7V)!NJsUMnff%4Eq3Jft6@$Z#yZMnb#CYFeR~Z>(!E{(=VA4qF~3QL%&bi?EYk9 z!?RG$=B#y3d9X<5yr2}O# zs4|b6TC+Wp(DV2C)K=i<^=At;EsnP8iUEI8b)Z#oVmKSji&cUCMXPoUFk6mE%Xpa_ zcF-G#mO=x=j8%qe`B+b*!O$p0_M5bqY;D{f7pwXxNSwST^!LKTiH{76IHuRQ&tAP_PZoU`*6%!$~ zX?EIJ;Dd#IU&Vxr?N!*mz7-eBtrO`ceH*ly`FXWT#qYb)t-%x~w?n~m!wKi4=Fwhq z7MsoHPV#IJi3}5q9M=-di{k=Z;-Bur{{ z`@2678|Xm^I_eHxOjh-3Gflr`&feGv?b;3JDY5=WC3=U3Qua(&DvHNf6hueouW^e( zc+19QVhf$VJHnzsA;qfuCv4EKIMW`-+6DI^kk^MZ^?}e&P+**NPjTR7uR(Rr7ulHM zr6tY8Xi^^tKUdI<3@yeumkVoJz_b|muE<}uBFBAb9g{Is#n>_YialSCmp%47E`7X# z;@B;u<2); zbz!im&x4qBcyNjAX*s82+j=JsW-*?c@2^;z%Jvc#t{YNI$1hL+nA#>p9TS{_9kcd^ zk)2_=mu>h_D0L0)RP5CtjI2U_*Rlmf;Ga6m=pVFO*g9?=7^73d2?|SNnPCTy5 z(Sl-F34&(A0O3NmNw`E|BM zl|~LmC9-pQp!f5=!(chR0|S$LuYfG>;N+RTNO6!uyj3A86O;&1nMyx!!te9Vfwt+0 zf;r;c`Zzd3KDUO{yz^Qm5ese3jwx8bmp5La0@85voUXfzadI~e&)XpV6!o5~ApJy5 zo;(RynPVX@b@tMICHnLp``}}{58-qot;mJ@L6vEBDM4p7sn8W3Uqz2QnnCv|wvYRO zs`m-HZ*74PK`^=%2kQM(yD=w)Pe05$3w*{`mu0^@FbS9vbtZGFKqiExee9r95#0Sk z?mk4{Z7mQBBhX#iSmjjGz3<8c_^Ny*xuz0A-kF-O3Lqn3kB|TJ2zB>TXCgnZP3_~g zWnr-lV+OUuuP+}lVTEQOx{<(b*T$Ap+5)QivCNr)i>0E&?b60EHa;%bP5yIL-La1b-l<}WEbAx6R_52w{t(jI0iW&SN$FJzvBCqN z^@FS{LNzxErVop%NW9wLIM=rC;9fs#q&S7S5&d(VpOop7!932-58iT9ozbniEZ~Z2 z;BXM%clYr(2`gs6=t4u~}c_i2|!I?{IzhCN9RBG0U6%-<3dXrU0r&#=s5shS}Xn+$&=*rH?8v&}mhW(dB^uHO_f29J*&yt8W}R=_O4Q}pFtb*Tb> zrE`hb&c6go2Bk$@i<$qaPm-0#UyNMV!Lg_y&VZ!q(hYw9${QfyACIhv&C@VizXnEe z5s4(>Mk6CVeq8*3>E&;`fg_Q-_t^Sn5!aXRJ(g?*%;I_`g)Cid z`CG?%rI$jW8YlSpM}p#W5Xhf|ov)oK*s+2gN7#u4JK=B#5q8k!o`1Ah-bDFmIAx&yMZg7olK!Aam8Cx0^TylKyf3yg) A#Q*>R literal 0 HcmV?d00001 diff --git a/test/shots_out/multidrop_single.png b/test/shots_out/multidrop_single.png new file mode 100644 index 0000000000000000000000000000000000000000..dc5484aea0e60b79f307efb06199779390dc64ba GIT binary patch literal 69560 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 literal 0 HcmV?d00001 diff --git a/test/shots_out/multidrop_stop1.png b/test/shots_out/multidrop_stop1.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7ca2dafbac04eaf12ab58ee060ccfe46880625 GIT binary patch literal 79915 zcmeFY^;=ZY8a50FNJ>da#}LvD(hUOw(jncAbc52}3eu%CLk%6$C6YsjbV+xAn{)Jh z?~m^v@D3LjF?;r2>se3S>wfMP@lHt=6O9-R4h{|zEGMN32Zxvl2lwm_1sV7cS#5GN z@B`6B0<4Y#ynIp2!hyfxT~uWy;3|HR?!duO!hxm4)jc!*EPA+-Xv`rWSz1im4Yl^T zGrq8d^RjkUGDa#Bpf`|0a2^K92(%>jBC#~-`?3QY69 zE;4)h4e4s>*D)w_oF)0zeRZJzb`ZljKqI$$l)V{rT)GO(1Jk! zu2BsU$Hn>kD(<9=_;?4AyS>!^GtQGc{9kzvciREn z8+sH3N)&8L_51l_CphekeWm|;*?O2=17n2oh<{`V{|uja`a_l`V$}MZ z!lQM8LT*C;$G?FT7*_P|kq{0;pwF|@ru8MOMk;er(OvtjiQCNP_-`~+Y$i$CQhd#H z{1b$?>xvkOyWxO&q)`KlgzOa)rGj78j#~4K+P)(G8+7_~kqBo3m}ll;jGt^-NW}XLnJ@sUPTx6C`4Baq88n^y zIc1%C=M9vR@lrUv>G+!H&uATuJ?BsT4}LO2XThx4 z`)x8ewiHPj0asx&3@!+S6_JOil1MrSQV}!rL;-A(eIE69hNRX!)VL!$y=NW|5`;&j;laWi-)W3#rG+W0bGmQ~qJO*bKv^cde+JQ*aH zcu#^J{sK!}Pn|(Z$(-iDtBWQFJcpGLV{C=0vb{A06jR2^`DS&Jm%e0La$JsjHBTZJ zc7!SY-%-89h|U$*cEhbcRWPc^*rkiaMMNn}Uq_S$y^iOOrAlmbo*|L^TQl;0=fU)v zwx;JlLmJgLLroo%+2u+$RfP+f*c8GIg08G+wEi~+)j�ktXlO0tKF=0h}O^AS}uz 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

    ;Y%H}Ru&VjMw--ck5a~I3w`tSUR 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%0AxeWIeI*ox!!JHQ4G+M4i5aN6)ANV&Nbx!EyD zjD4}o3g>t-c34@zM@|q&r>F4s_G)uvIo3#Y>6IfEPbN)Fcgmq)W{T?-*Hzn-elX{s zZIm@bN2=dnY#)+p!--Qdp^}?{tF*s}TBb&GvQtacPNPdQql5&N$ESATgmTGWLdy+i z)~+Lvdh!LmvKE1o z^B2Z{G;T06&}+=P&SbXs2Oz#F`ifjXHWc>Tu1{Yt3}!{Mrac9AhjPDkbE}&d_1#F{ zob5vQTX1^o8zdmVDq8uKIAy0wV<)ZT{bu{f2xANKDqP@a3t#!GtgUqsmk%v&+$$}d z=6(rXuKQ+x;<*eKXmoPAn@4z!_|()AKnpMwP=lGi}M@dIma|2a&Z}`n zH#5RmR{fZk-N$A6b@ih+BDKz1Z;saYM0*!+gtPOiek|a7TUzG2o>NB;PzT5fL`rDVUmeZeq@2Es> zNzw)Q!BdblVHurq8OD(l|7P=w;!IUG4acmLh9fPCRR(@63<&5kF)4T<(&|ZCX0T_^ z17TuG;rKA;;sTur-uLmt?Mm`#o~<`;3Dns=X3@PX_jRO0Ydjc0UlZ{3wf?r%bl;Dd z8dfIyQLz(<&fT;q-QXBDX(=Y`tsAy0ctRO$?^_x_sVwP57LrXBFMfw|Zix+f$g>-g z$QZ8Oys%@y(^Yiz#j*IJQC|gGam3?NC@DyO$j+p8Om+5af%3AQ;cR>IR(P8uwMOh}eq5)=W*pTA`#Nj&=)pnc-YTGv zxMk+?YHi(ex(0VKTO3|W{9ZZ5!hPDJ8g5<bn3Jdu_wFHNytwPP2nWp@#J# zP(T{o>F#$MRf*L5W5;osJ^W-)UXB=7wDmdWFVRv*dhwiGqfi|;UbZ~IV)|%*w$16C zcJd>mB3Q9mpA}beSNC1ApF@;)`*jNsdeT{BKHzw02RPe4KRK$oGdWL+p54pq=L5tB z*Rb7AubYywb-}g+l4ddCPRA~GK7N$CW-rL!aITHB9mqK*8B+hBZ;TFv4MgebQ;sl1 z#9UAVssCDiz%puOuKYGm5pniguZ#iP?-97_ojSww@ef&U-TjPn>d5reGlPV|4-+c3 z_#Zszuxwa5Uc-F4Kl}0}L5+gDrB*$8c;c(9)^+`& z`_61;+{wwq{+~ngnSY8mMlX@ai!%+sfYcrXr1lg-&DP2AP{{kG5O$^{5jCnxU&7X_ zTb3J=yvpx_&M|*Tz2)tu;uIXF2mts`Z-0!#N#SbrrH>@Lzhh98wlf=7GJ3=)Q2tYf zbD+`_U};kj%|gXr4^;!8aZmaTm3uj@J}Yv9+OuD4gsC5js4y}+)UDlWJX z-FiKg^j-I2Rq=r=NV(_(b%G~+ltnKhspUK;Q&OWF#A;t=9;)%bCUwq~Bod)U^Rzpc?taD1qRVxsKqGu_e4B3SCF?E3Aq5jhzF&mpE%5)I zQ%7dRJ893cfIbv^A8YzI9*;SYvlrq4aBx-}0h&D`R3^1Ikwj$vyJ4;Y>f7&vzk<9k~}Q#`Gv0VR3{hd(puDlVHs2dmeh z()I8qR?glfv%vE$s@r<2GvgEA8{q~=kX#~Z(6kNAUT--xz-891Zce9<8(-fzJHfZG zH(~)=@=mz(W`hH)EY9^sX*OfNIq_g*ej1EbO!7=>_GVJMVOLqOyxWSl^_|{c8d~*_ zg?-RsSlRBgS-c`;@lkp7)9~O);KDWO{5OO~t+T&VVkip8H^plTE>~^z&-Trxzd{8` z#P729Faw??LjK`^iH|{7E6BlHn~cpoCQ=SG`G~T~8Q{Js>5FCjHT5U#}%4YL@)n{WB<^Pjkip>FM@c<9EZaYODa| zt9rA?z)3H@6k5EVbNgT^Wczd2kOUNN<#(Ht=VCB4H4OSsi-%;OYzjFhxnzW1z)# zyJIO$=!nEYIBl^y5ha? zV!Kl@&dj~9e-3=!(@X6eKPkL}txUp^@-J72ZPfO_ctPk@S=X6}V%@>mm~*aKgj2G_ z@a&i4k?o}n*JFp$Hqj^t?^!vOuRVNxHb>ix5d*nPs59C00-6ZGOG;+(&DaFf1*B=Ls zS8Q=i0t(Rb4jP{~Kz3sKq(>gCgn&*uTPA#EOOo{S&`Oqk8F98)*sfEQEUi)7V*yGL?GS8jk9=b5weq*F?D^u#M(7wgHG!veR=O(4R%qyK6$)-EaT7f>!qeah_bV zpx~Hx)of?PYSPYC2}PROEE|2=Ryo!7Q8XTYc zqqIs~fStmf_G4Vem6^fjF_B@kJyj`-S=j{&N{?({01gx*0<@-bB0*~^_MXqy0M`-$ z2jUuE7a*=s;)12)m5op_Wm7mRpE96r0O+9iwSz2N)H(Pm^OeJUHg1yG2%PT#8~l71 zQR(;XkW<13BMe+}<>)@MQ_{!Zw)HhBQ7zfUkh8=Qy*$0R{?vbsEnc}2HHoJpL>&#e z;=;$N3983+MbNG1AHVHg(X}hw;<{V#q-d_x{!5{uE4;hJaWmVO27d~mLB=g_&^24U zxdpk(<}Qp}eum3BAu_B5asaZm@od{C?L7f|EYC}vUq;#%zS%6GU6EXIM#es+?j__h z-o3%$4QO^#&W8ySp+g>d9~vL|pH6eYB46HpT`6v=xi40U>nWKYnl`Q69kfSe9l!m? zLMRv0Dd`*#CgV=Ja!{!J>sIt=Mw1F*NgfwajfE<4iQTT^M{1JZpd&tf)hb$YdfiE9 zd_{pDMl%Vzp#u&_ZwVEyR*Tg%kGI2%D_)-M4_%S>wq8%7+wwUiY?Wwy0d0lf|B8an zn3_@rombzx&b-I%Gl^(~!#8~E<^(a2ub|hDecNxu04f%4%iBRjiYa6LNH-RGvUAe8 zL%mtDuI?~~d|#lYEF95sek{ZXKnkE~#GIYaMuFC)PCVvwGhEbtDw-buMYK zuIj~-}- z&Xh92hFAvTe-Ar<)L8-&9u4M zbEhek%K1xJUx4ZLYaA!&&6`zXVr^UHn8@xQel*>M@nZ2G8VBVmxy45E?9WDvw$0aH zx)9Q8cAGB}R@h?J>@_XiyV$J9>64#qRkhbU zdAmPbz3$t-D$TFU`T}sWMczrN&&N9Ja=F22sPQ9F&U=8<{ONJw5N(uo!gkY}D3OnFw*&wTI+G0gW{1qZV9R_it~x=>kz9^em*p{?*OR z_vnkm+|rkCPu|A0bqS#?DondJD zZ1NL(wjfbOHp7sR(7s?i0@ULg9m_d`Z!Tw8jGgxYj3T4A5aYcMEg22C+Ycdsy9%vr z&fI#8^b}|SroXVzPz5qFoB2^z?2A4+P13r-t83fFdNVU6^!Fe2L}KGSQ!pltU>VT5 zu34QIa+Js(H2BH9dsD&eQc&GSkCbJOQFf3758 zNCM=t-3HoNMotpMF6QKad2zqkt zLDvoBA8ZY;oa7-ZIE7VJZK(X)~)K&R~hq>PE3%Nooh^Ex2okkX>&zCt zJ+-?7rHBfZCx=s;11p1STe&+i;2EL4Cs(ZgGuI-N$=!I5Rx>GD_NzU``2tBY?F$Z1 z{!V8k_sO3F=rH2z`6^c1*s03-MV&+EJ~@u@`slbc)csU=Sas4P-!6XiepFFn#2=hb5Rp2BKTLLO^ACVno|9to3>+xR>jj zNC7Bg3;+I-K89CxgwF6-Y!-O((=4qO4Gb1bDFA9FgaC!(Of_I}ic6#eUomIDe>qvf z0Y18WmTPs(BdrV}8l*7s`3TcHTask0_3z+97Ff9?3Y%;_P_be>nlgxg`Zp+@cyl~4-L zeAFM3RG_Y&AN3z~>|NSpPkIGS1(I)H2;d91WYo0WW-3OBugV%tZj3wtb=Md`4Ro@N zl@m#aQ_MoqTo2+S#R!?upJH#>QHIcU*z$H&98-|Ay-r|?A$K7T_t=irYeSG_^O)&> zS!p+FeY8++iu|d#aJcVQw0)LPD0W&&0FVkGNY)u4fIOQN|iZAbo1GST7RAU{+z+ zLXAPVBXso77wojC{EA8>a!Aosl?^>l^59{Sk_R82T+}q_iC`TdkSRFfW23I1s_s!|Gn-i3e zQdR2xXCjn&3{4k{?D6=3xZWBk%b&!jtaGvSV~C)wmZ9e z;;U^tNKMcNsKoMuelS1V?!S1EH!gC|zsvJAdyFI$C_;GO zqD17h3MFU6YWd6veasA7DSW15&-ms06LUa=XgnO{xDgl}Sb%7R&a4I!E0)88kv`sJ z1P75ZF`o8|g4a+0*bXRkfr4Wc0Phnbk4kczZ-VVnxF5X8XG880h@Q*|{m?v6$NS9& zinTw}E}b#921HyEKvn`mC0!K5a<1h|$tT!JN>p<8^;)5O83=g$&}6do%|mtR1}qtwSmh_a)3QHE>lHFN2@Pz-=t zMxR#E=E81M;Z5ef>?-#qp~GpS(#Kdig(P61`R++foet0KsO3=ZRT#km8fL%*@!9@u zd?5S-EmPt!Kh>;c(H3Jeu?k9(Vs>y7Zlp*ypTbq2M3JO_k4M48^ft&%fGDSbJ^1pO z+6Q4i+Y|?u8~jHPM(~L-?@%!#8KwTG;`#Nw*$oMhp6}fU$Ss}$?$^l1-~+>m93=u- zpo9PfNe}ixD)Mm-P1omWZxqdG$Dg!WiZD{d+KOx`{in$~`pC>!8AKL(YBd-Y8G{@w zME*ib2~X)tEny51^)}xOI4q$VKee5>SM4M{P+H<&szh%`*i%b?VbXwT<5e`vc5)38 z8e0u1TxnqUI%nlPZ*Vj*dxE?zaLQ$s<9;54$}CH z9^5hU$0I6|Nu1zO?KiM%=iN(pN~%*Dj7u}vtiWo7vI*&>CAYo|n{*iEPPuTK6K3reIOSc0?;T zd2ppAOigx5RR-SD`=5AIqpq&i9_t6XE6^rA0i$_5(sN-P&Shlq^nTBYhRhV%5J5z? zV}I4Fr`{)`+J>9k`T0K)q7E|!7T~c0?W6{mtHuZ?o^lD3hP3gAHaW)RTxN)1`pk*CdE#$0H~?-f_QlUTRDJf|sM zYzh74>9Z^~M^togZCxKn6i6k6rM7wPm`t+oCJmC0|tCsek1{h0;$(zRyMK*3y{c*R+@iv#8sn!pq% zhe7P`jfOv0s453hO8QS9SA0*P20sDXw`@>phV&D|x>8u96k;O5gR`G05U@BU4Wk9) z@iCskK$>9@6Du`LuKs`0&XTN3F>EV`jdCyDBn9)<`~xPsr_2)8_85wf=P%wnyC@mi zE|ro4R(vZCXjT3vugk@_C!qhVq^yj4kf6hN*Kq_(iQh|6{m+{35O9wIM^sXSOqy)TR&%{}aCC3tUl4%@O ze|a=1lWd)CvK5dpbu9~!X6vGsy5T&~KJPwaJ%O~~=uWu}N(swo|?N{LS z;5qF6^EX{Ex+t=POE|0~v&54Xyd_aDV3J!b>g`LTBhdG=->GOt^@?3f4;<-b1oKX|K>Mz{JXde#rl6qH{2_1IbRW%*s5;(xZ4kCX!K? zdXlXYA`|3USqP-p;y|==!38hc!ivjw(44q^Wcmuk_<+Lh{C$`OUmg=`WWuD*@DpDF z7P;2XWI~vLM;NUDwybxLP(qko|1cL@n&cmJKr~^50PVTmWc_+{5>Z+KF_$kwn}Vq!L(=Db?=ZJ!T9H!W!Ez?}rVQ zWn!`1Smwbu5meoN8LSpM5W3p<2!<>fxE%&;F;W=|vFWgf7W^_vWmE(Yg94c_#v%+l z&;>EB>1{lk8m5;`tHTKcPm)e}D)3nE_DWb@%FmpSERiZ3NpV&1tW%BPN&fdIUAE?~ zZ}@{j3b=ZjLm-#;PBEXi3!+J;xI0gt3O$y9+)CZMJT1XJync?q&F9<6N}fP4{79Df z*(aY^A=yH=FO2V5zma3BFoaP8NtVirMQYw%AJBpS6QrX_5slof2;P$(3?p(WM?bP@ zY2YUsMo6zmM!_Ipds8SoT;tauvM}dka{#S3JzGo}{|GFZd+2-t_qB^P0f?s)FgnGJ z^LD)vw;!zF+68JUyaYVz==|wNFcm{vH6mH29%edvkrYOb5-CeQHcs%E_Z1dVmA+|q ze(S1a*JBYNYj{JAaUYoz=-CPMuEj{po!d>fUwUwLSxejb6ELWhSrsRvy}$fn-;w)= zpFWbI&>Q(-YTJM+Q5(RXR`Mm#C85DB%Zt&y__p43boFix{rvwto(?o%?%R55KDe4Hti@KyY!^+|(zQc(9&5K_Ro514gp z6yA?Y7*2)Ea@`Kge8sm+qM6gTzgYC|eqc=>W7gtCfIwu|4}>2T!>Zp=aA$EwI)*iT z3tznZxfj&q6gJ_6*+l6fg@JeM3JkWcsJ(LgM^gv^ReRkM<(#KpVUYh7{Fo6=uh!su>$tgn)(*fKyImEmi7X} z8627+5b+TF7s&C|W>EAJVjd+jp+D9LmP#^Dz~ZHhzWDsjcTxQvKIs zF4nKp50Jvb!oUjG;uOmP^JC|(XW*8=LBg}(`>bmTvgODjVnL#l81ox9S3X_=0qkyGT5MCHbYRSr{&#T+Ke*@{IO9po@_O2|Ylk+~_Y z=CFoh$RXjLncXPIoRd?`_+Gp3K7Yjb@#yu-et2Kk>$)PXbJ+J%u!NBj3 zZZ%nQiJrV|D6spaO)0^>_HKhF+cZ$t@s(@;)JXhpTRVQ=QlUJ9d3|_lxJDsO?DuC#IcMmw? z)R}2nh#P53I<1gd2H6C?s|k2hQcmYN+o9dAqct9yo_aTOxvu=y4t~DP0Zr?^-(5`= zlfkpYkkRHqwxMdt;lQQ$kB44$C(SG>XRTa4Vg1+A25`l`I_>QqH(Lk?nBF~~|w0!bI0ZcwPivy}dCN*|OYBw-R* zO_;m}z{GyYx{pft)+;9jJ?k2)9(_xI+~j)S(lMb}=>+{P050@z*CNzT

    5yWpzG}IG` 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 D+^khG literal 0 HcmV?d00001 diff --git a/test/shots_out/multidrop_stop2.png b/test/shots_out/multidrop_stop2.png new file mode 100644 index 0000000000000000000000000000000000000000..366f6065ece7085c0716856b0ec4bf61980226f4 GIT binary patch literal 75887 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 literal 0 HcmV?d00001 diff --git a/test/task_profile_test.dart b/test/task_profile_test.dart new file mode 100644 index 0000000..d2e95ef --- /dev/null +++ b/test/task_profile_test.dart @@ -0,0 +1,253 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:miler/data/task_profile.dart'; + +/// One rider, mixed work, and every row deciding its own workflow. +/// +/// These tests exist because the app used to answer "what kind of work is +/// this?" by looking at the **rider** — his tenant, resolved once at login into +/// a single app-wide service line. That answer is the same for every row on the +/// screen, so a customer pickup sitting beside a kitchen collection either took +/// the kitchen's rules or disappeared. The rider is one person with a mixed +/// queue; the queue decides. +/// +/// Every input below is what `GET /miler/bookings` actually sends. + +Map _row({ + String? nextAction, + String? pickupSourceType, + String? stopType, +}) => { + if (nextAction != null) 'next_action': nextAction, + if (pickupSourceType != null) 'pickup_source_type': pickupSourceType, + if (stopType != null) 'stoptype': stopType, +}; + +void main() { + group('a row is classified from the server\'s own words', () { + test('a customer door pickup is a customer pickup', () { + final t = TaskProfile.of( + _row(nextAction: 'pickup', pickupSourceType: 'customer'), + ); + expect(t.kind, WorkKind.customerPickup); + expect(t.capturesShipmentDetails, isTrue); + expect(t.groupsBySource, isFalse); + }); + + test('a kitchen load is a source pickup, and groups', () { + final t = TaskProfile.of( + _row(nextAction: 'pickup', pickupSourceType: 'kitchen'), + ); + expect(t.kind, WorkKind.sourcePickup); + expect(t.groupsBySource, isTrue); + expect(t.capturesShipmentDetails, isFalse); + }); + + test('a base collection is a source pickup too', () { + expect( + TaskProfile.of(_row(nextAction: 'pickup', pickupSourceType: 'hub')).kind, + WorkKind.sourcePickup, + ); + }); + + test('carrying it to a receiver is a delivery', () { + expect( + TaskProfile.of(_row(nextAction: 'deliver')).kind, + WorkKind.customerDelivery, + ); + expect( + TaskProfile.of(_row(nextAction: 'start_delivery')).kind, + WorkKind.customerDelivery, + ); + }); + + test('carrying it to a base is a handover', () { + final t = TaskProfile.of(_row(nextAction: 'inward_at_hub')); + expect(t.kind, WorkKind.hubHandover); + expect(t.endsAtHub, isTrue); + expect(t.deliversToCustomer, isFalse); + }); + + test('terminal rows are done', () { + expect(TaskProfile.of(_row(nextAction: 'none')).kind, WorkKind.done); + expect( + TaskProfile.of(_row(nextAction: 'handed_to_hub')).kind, + WorkKind.done, + ); + }); + + test('collected with no stated leg is custody-without-destination', () { + // NextLeg.unknown's case: it is in his bag and nobody has said where it + // goes. Not a delivery (no destination to offer) and not done (he is + // still holding it). + final t = TaskProfile.of(_row(stopType: 'delivery')); + expect(t.kind, WorkKind.unknown); + expect(t.deliversToCustomer, isFalse); + expect(t.endsAtHub, isFalse); + }); + + test('an unheard-of source takes the safe shape, never a guess', () { + final t = TaskProfile.of( + _row(nextAction: 'pickup', pickupSourceType: 'something_new'), + ); + expect(t.kind, WorkKind.unknown); + expect( + t.capturesShipmentDetails, + isFalse, + reason: 'the door-capture flow is a claim this row has not earned', + ); + expect( + t.groupsBySource, + isFalse, + reason: 'nor is a heading it can be filed under', + ); + }); + + test('a row with no type fields at all does not group', () { + // Every fixture written before pickup_source_type shipped, and every + // booking still in flight from then. This defaulted to sourcePickup once + // and split a one-base logistics day into a group per customer -- + // hub_pickup_group_test.dart is the test that caught it. + final legacy = { + 'orderid': '9', + 'pickupid': '9', + 'pickupaddress': 'RS Puram, Coimbatore', + 'type': 'pickup', + }; + final t = TaskProfile.of(legacy); + expect(t.kind, WorkKind.unknown); + expect(t.groupsBySource, isFalse); + expect(t.capturesShipmentDetails, isFalse); + }); + + test('the leg outranks where it was collected from', () { + // Picked up at a customer's door, now in the bag on its way to a base. + // It is a handover, not a pickup — it stopped being a pickup the moment + // it was collected, and only the leg knows that. + final t = TaskProfile.of( + _row(nextAction: 'inward_at_hub', pickupSourceType: 'customer'), + ); + expect(t.kind, WorkKind.hubHandover); + }); + }); + + group('surfaces', () { + test('an unaccepted customer pickup waits on Home', () { + final t = TaskProfile.of( + _row(nextAction: 'pickup', pickupSourceType: 'customer'), + ); + expect(t.surfaceWhen(accepted: false), WorkSurface.home); + }); + + test('accepting a customer pickup moves it to Bookings', () { + final t = TaskProfile.of( + _row(nextAction: 'pickup', pickupSourceType: 'customer'), + ); + expect(t.surfaceWhen(accepted: true), WorkSurface.bookings); + }); + + test('a kitchen load stays on Home even once accepted', () { + // Accepting a manifest puts nothing in his hands. It leaves Home when he + // is actually carrying it, not when he agrees to fetch it. + final t = TaskProfile.of( + _row(nextAction: 'pickup', pickupSourceType: 'kitchen'), + ); + expect(t.surfaceWhen(accepted: true), WorkSurface.home); + }); + + test('anything in custody is on Deliveries', () { + for (final action in ['deliver', 'start_delivery', 'inward_at_hub']) { + expect( + TaskProfile.of(_row(nextAction: action)).surfaceWhen(accepted: true), + WorkSurface.deliveries, + reason: action, + ); + } + expect( + TaskProfile.of(_row(stopType: 'delivery')).surfaceWhen(accepted: true), + WorkSurface.deliveries, + ); + }); + + test('finished work is on Activity', () { + expect( + TaskProfile.of(_row(nextAction: 'none')).surfaceWhen(accepted: true), + WorkSurface.activity, + ); + }); + }); + + group('the mixed shift', () { + // The case the architecture exists for: one rider around RS Puram holding + // ten meal parcels to collect at a Gandhipuram kitchen and five customer + // pickups at five different doors, all at once. + final queue = >[ + for (var i = 0; i < 10; i++) + _row(nextAction: 'pickup', pickupSourceType: 'kitchen'), + for (var i = 0; i < 5; i++) + _row(nextAction: 'pickup', pickupSourceType: 'customer'), + ]; + + test('all fifteen survive classification', () { + final kinds = queue.map((r) => TaskProfile.of(r).kind).toList(); + expect(kinds.length, 15); + expect(kinds.where((k) => k == WorkKind.sourcePickup).length, 10); + expect(kinds.where((k) => k == WorkKind.customerPickup).length, 5); + expect( + kinds.contains(WorkKind.unknown), + isFalse, + reason: 'nothing falls through to the safe shape when the server spoke', + ); + }); + + test('accepting the five does not move the ten', () { + final surfaces = queue + .map((r) => TaskProfile.of(r).surfaceWhen(accepted: true)) + .toList(); + expect(surfaces.where((s) => s == WorkSurface.home).length, 10); + expect(surfaces.where((s) => s == WorkSurface.bookings).length, 5); + }); + + test('collected meal parcels coexist with a CX handover on Deliveries', () { + final afterCollection = >[ + for (var i = 0; i < 10; i++) _row(nextAction: 'deliver'), + // The CX pickup, now completed, has become a base handover. + _row(nextAction: 'inward_at_hub', pickupSourceType: 'customer'), + ]; + final surfaces = afterCollection + .map((r) => TaskProfile.of(r).surfaceWhen(accepted: true)) + .toSet(); + expect(surfaces, {WorkSurface.deliveries}); + }); + }); + + group('a trip is described by what is actually in it', () { + Map row({String? a, String? src}) => { + if (a != null) 'next_action': a, + if (src != null) 'pickup_source_type': src, + }; + + test('a mixed trip both groups and ends at a base', () { + final stops = [ + row(a: 'pickup', src: 'kitchen'), + row(a: 'pickup', src: 'customer'), + row(a: 'inward_at_hub'), + ]; + expect(TripShape.groupsBySource(stops), isTrue); + expect(TripShape.endsAtHub(stops), isTrue); + expect(TripShape.hasCustomerPickup(stops), isTrue); + }); + + test('one parcel bound for a base ends the whole day at that base', () { + // `any`, not `every`. Telling a rider "END · HOME" while he is still + // carrying something the network needs is the failure worth avoiding. + final stops = [row(a: 'deliver'), row(a: 'inward_at_hub')]; + expect(TripShape.endsAtHub(stops), isTrue); + }); + + test('a pure meal round neither groups to a base nor returns to one', () { + final stops = [row(a: 'deliver'), row(a: 'deliver')]; + expect(TripShape.endsAtHub(stops), isFalse); + expect(TripShape.hasCustomerPickup(stops), isFalse); + }); + }); +} diff --git a/test/work_scope_test.dart b/test/work_scope_test.dart index 4a1e277..c36b727 100644 --- a/test/work_scope_test.dart +++ b/test/work_scope_test.dart @@ -8,7 +8,7 @@ import 'package:miler/data/service_profile.dart'; import 'package:miler/data/work_scope.dart'; /// ───────────────────────────────────────────────────────────────────────── -/// TWO LINES, ONE HANDSET, NO MIXING +/// ONE RIDER, ONE DRAWER /// /// Miler runs a milk-man round and a logistics day off one app and one login, /// and it kept finished work under *global* SharedPreferences keys — @@ -18,75 +18,81 @@ import 'package:miler/data/work_scope.dart'; /// /// These pin the ownership rule at the data boundary. Nothing here filters on /// a display string — no kitchen names, no "Milk", no screen titles — only on -/// identity the session can prove: rider, tenant, line. +/// identity the session can prove: rider and tenant. +/// +/// ── The line came OUT of the key, deliberately ── +/// +/// It used to be a third part: `completed_bookings::u38.t13.milkMan`. That was +/// right while a rider was one kind of rider for the life of his account. He is +/// not. One Miler carries meal parcels, logistics collections and customer +/// pickups in the same shift, and keying his records by line split one day +/// across two drawers — so work he finished an hour ago vanished when the app +/// resolved him differently. Two tests below used to assert that split and now +/// assert the opposite; they are the record of the rule changing. /// ───────────────────────────────────────────────────────────────────────── void main() { - const milkA = WorkScope(userId: 38, tenantId: 13, line: ServiceLine.milkMan); - const logisticsA = WorkScope( - userId: 38, - tenantId: 13, - line: ServiceLine.parcel, - ); - const milkB = WorkScope(userId: 99, tenantId: 13, line: ServiceLine.milkMan); - const otherTenant = WorkScope( - userId: 38, - tenantId: 77, - line: ServiceLine.milkMan, - ); + const riderA = WorkScope(userId: 38, tenantId: 13); + const riderB = WorkScope(userId: 99, tenantId: 13); + const otherTenant = WorkScope(userId: 38, tenantId: 77); group('the key', () { - test('rider, tenant and line each change the drawer', () { + test('rider and tenant each change the drawer', () { final keys = { - milkA.scoped('completed_bookings'), - logisticsA.scoped('completed_bookings'), - milkB.scoped('completed_bookings'), + riderA.scoped('completed_bookings'), + riderB.scoped('completed_bookings'), otherTenant.scoped('completed_bookings'), }; expect( keys.length, - 4, + 3, reason: - 'all three identity parts must be in the key — two scopes ' - 'sharing a key is the leak itself', + 'both identity parts must be in the key — two scopes sharing a ' + 'key is the leak itself', ); expect( - milkA.scoped('completed_bookings'), + riderA.scoped('completed_bookings'), contains('completed_bookings'), ); }); + test('the line is no longer part of the drawer', () { + expect( + riderA.key, + isNot(contains('milkMan')), + reason: 'one rider, one day, one drawer — whatever he is carrying', + ); + for (final legacy in riderA.legacyScopedKeys('completed_bookings')) { + expect(legacy, isNot(riderA.scoped('completed_bookings'))); + expect(legacy, startsWith('completed_bookings::u38.t13.')); + } + }); + test('the same session resolves to the same drawer', () { expect( - milkA.scoped('skipped_bookings'), - const WorkScope( - userId: 38, - tenantId: 13, - line: ServiceLine.milkMan, - ).scoped('skipped_bookings'), + riderA.scoped('skipped_bookings'), + const WorkScope(userId: 38, tenantId: 13).scoped('skipped_bookings'), ); }); }); group('ownership', () { - test('a milk-man record is not a logistics record', () { - final row = milkA.stamp({'orderid': 'A1'}); - expect(milkA.owns(row), isTrue); - expect( - logisticsA.owns(row), - isFalse, - reason: 'same rider, same tenant, other LINE — must not cross', - ); - expect(logisticsA.excludes(row), isTrue); + test('one rider owns his record whatever he was carrying', () { + // This asserted the opposite once: same rider, same tenant, other line + // "must not cross". Under one-Miler that rule loses him his own work — + // a meal drop and a parcel collection in the same shift are both his. + final row = riderA.stamp({'orderid': 'A1'}); + expect(riderA.owns(row), isTrue); + expect(riderA.excludes(row), isFalse); }); test('another rider cannot claim it', () { - final row = milkA.stamp({'orderid': 'A1'}); - expect(milkB.owns(row), isFalse); - expect(milkB.excludes(row), isTrue); + final row = riderA.stamp({'orderid': 'A1'}); + expect(riderB.owns(row), isFalse); + expect(riderB.excludes(row), isTrue); }); test('another tenant cannot claim it', () { - final row = milkA.stamp({'orderid': 'A1'}); + final row = riderA.stamp({'orderid': 'A1'}); expect(otherTenant.owns(row), isFalse); expect(otherTenant.excludes(row), isTrue); }); @@ -94,8 +100,8 @@ void main() { test('the API stamps its own identity and it is honoured', () { // Rows straight off `/miler/assignments` carry the rider; no scope stamp // is involved and it must still be respected. - expect(milkA.excludes({'orderid': 'A1', 'mileruserid': 99}), isTrue); - expect(milkA.excludes({'orderid': 'A1', 'mileruserid': 38}), isFalse); + expect(riderA.excludes({'orderid': 'A1', 'mileruserid': 99}), isTrue); + expect(riderA.excludes({'orderid': 'A1', 'mileruserid': 38}), isFalse); }); test('silence is read differently by the two questions', () { @@ -103,8 +109,8 @@ void main() { // rows, where inventing ownership IS the leak), `excludes` keeps it // (used on rows the API just returned for this session). final bare = {'orderid': 'A1'}; - expect(milkA.owns(bare), isFalse); - expect(milkA.excludes(bare), isFalse); + expect(riderA.owns(bare), isFalse); + expect(riderA.excludes(bare), isFalse); }); }); @@ -141,34 +147,35 @@ void main() { }, ); - test('switching line cannot expose the other line’s history', () async { + test('a mixed shift is one history, not two', () async { + // The inverse of what this asserted before. One Miler collects meals and + // parcels in the same shift; his Activity is his day, and a label the app + // resolved him with must not hide half of it. SharedPreferences.setMockInitialValues({'userid': 38}); ServiceProfile.setActive(ServiceProfile.milkMan); await addCompletedBookings([ {'orderid': 'ROUND-1'}, ], terminalStatus: 'delivered'); - expect( - (await getCompletedBookings()).map((r) => r['orderid']), - contains('ROUND-1'), - ); - // Same rider, same tenant, logistics day. ServiceProfile.setActive(ServiceProfile.parcel); - expect( - (await getCompletedBookings()).where((r) => r['orderid'] == 'ROUND-1'), - isEmpty, - reason: 'a milk-man delivery belongs only to milk-man Activity', - ); - - // And a logistics collection stays out of the round's history. await addCompletedBookings([ {'orderid': 'PARCEL-1'}, ], terminalStatus: 'picked'); + + final ids = (await getCompletedBookings()) + .map((r) => r['orderid']) + .toList(); + expect( + ids, + containsAll(['ROUND-1', 'PARCEL-1']), + reason: 'both were finished by rider 38 on tenant 13, in one shift', + ); + + // And it still holds from the other side. ServiceProfile.setActive(ServiceProfile.milkMan); expect( - (await getCompletedBookings()).where((r) => r['orderid'] == 'PARCEL-1'), - isEmpty, - reason: 'a logistics pickup belongs only to logistics Activity', + (await getCompletedBookings()).map((r) => r['orderid']), + containsAll(['ROUND-1', 'PARCEL-1']), ); }); @@ -179,7 +186,11 @@ void main() { ], terminalStatus: 'delivered'); final row = (await getCompletedBookings()).single; expect(row['scopeuserid'], 38); - expect(row['scopeline'], ServiceLine.milkMan.name); + expect( + row.containsKey('scopeline'), + isFalse, + reason: 'the line is not part of ownership any more', + ); }); test('logout empties this scope', () async { @@ -197,6 +208,52 @@ void main() { ); }); + test('work stored under the old line-suffixed key is not lost', () async { + // The upgrade a real rider takes mid-shift. His finished stops were + // written to `completed_bookings::u38.t13.milkMan`; the key no longer has + // that suffix. Dropping them would lose work he actually did, and unlike + // a global key these rows are NOT anonymous — the key itself names him. + final today = DateTime.now(); + final stamp = + '${today.year}-${today.month.toString().padLeft(2, '0')}-' + '${today.day.toString().padLeft(2, '0')}'; + + SharedPreferences.setMockInitialValues({ + 'userid': 38, + 'completed_bookings::u38.t0.milkMan': jsonEncode([ + {'orderid': 'OLD-MILK', 'completedday': stamp}, + ]), + 'completed_bookings::u38.t0.parcel': jsonEncode([ + {'orderid': 'OLD-PARCEL', 'completedday': stamp}, + ]), + }); + + // The migration has to be *run*. It is not lazy — `getCompletedBookings` + // reads the scoped key and nothing else — and this test called the reader + // without the migrator, so it was asserting against a drawer nobody had + // filled yet. `main()` awaits this on startup, before the first frame. + await migrateLegacyStores(); + + final rows = await getCompletedBookings(); + expect( + rows.map((r) => r['orderid']), + containsAll(['OLD-MILK', 'OLD-PARCEL']), + reason: 'both old drawers belong to rider 38 — merge, never drop', + ); + + // And the old keys are gone, so a second run cannot duplicate them. + final prefs = await SharedPreferences.getInstance(); + expect(prefs.containsKey('completed_bookings::u38.t0.milkMan'), isFalse); + expect(prefs.containsKey('completed_bookings::u38.t0.parcel'), isFalse); + expect( + (await getCompletedBookings()) + .where((r) => r['orderid'] == 'OLD-MILK') + .length, + 1, + reason: 'the merge must be idempotent', + ); + }); + test('unattributable legacy rows are dropped, not adopted', () async { // The pre-scoping global key, holding somebody's rows. Nothing on them // says whose, so adopting them would be inventing ownership.