miler map

This commit is contained in:
2026-09-09 12:55:23 +05:30
parent 074cc0eccf
commit 127fa062ed
143 changed files with 4315 additions and 2291 deletions

View File

@@ -269,6 +269,42 @@ bool get _handoffHoldIsPossible => ServiceProfile.active.endsAtHub;
Future<bool> releaseForDelivery(Map<String, dynamic> stop) async {
lastReleaseFailure = null;
// ── A hub-routed parcel has no customer round to start ──
//
// This is the gate that keeps a Gandhipuram → Chennai consignment out of the
// delivery lifecycle. `start-delivery` moves it to `Out_for_Delivery` against
// a receiver 500km away, 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.
//
// Asked of the order, never of the line — see [NextLegResolver]. The pivot's
// own instruction is read from the store because that is the only place the
// routing answer survives a rebuild; `GET /miler/bookings` does not carry it.
final leg = NextLegResolver.resolve(
stop,
pivotAction: (await getPivotNextActions())[MilkRun.idOf(stop)] ?? '',
);
if (leg.isHub) {
debugPrint('[NEXTLEG] release refused for ${MilkRun.idOf(stop)} — $leg');
lastReleaseFailure =
'This parcel goes back to base, not to the customer. Hand it over at '
'base — there is no delivery to start here.';
return false;
}
// ── `unknown` is NOT refused here, deliberately ──
//
// It would be the cautious-looking thing to do and it is the wrong thing.
// A row that says nothing is exactly the case the consignment re-read below
// exists for: `Created` and a blank status both arrive on a poll that is
// behind the rider, and the server is the judge of its own consignment.
// Refusing here short-circuits that read and strands a rider on a stop the
// server would have released — the failure `_handoffHoldIsPossible` already
// documents, arriving by a new route.
//
// Only [NextLeg.hub] is refused, because that is the one answer the server
// has actually given.
final consignmentId = await resolveConsignmentId(stop);
if (consignmentId.isEmpty) {
debugPrint('[MILKRUN] no consignment id for ${MilkRun.idOf(stop)}');
@@ -292,8 +328,7 @@ Future<bool> releaseForDelivery(Map<String, dynamic> stop) async {
// custody and refused, and no amount of sliding cleared a row that only the
// next poll was going to correct. It is asked directly instead — one request,
// on the one state where the row is not evidence.
if (state == ConsignmentState.unknown ||
state == ConsignmentState.created) {
if (state == ConsignmentState.unknown || state == ConsignmentState.created) {
state = await ConsignmentGate.stateOf(consignmentId);
}
if (state.isDeliverable || state.isDelivered) {