miler map
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:miler/Models/stop_status.dart';
|
||||
import 'package:miler/data/milk_run.dart';
|
||||
import 'package:miler/data/next_leg.dart';
|
||||
import 'package:miler/data/service_profile.dart';
|
||||
|
||||
/// ─────────────────────────────────────────────────────────────────────────
|
||||
@@ -91,10 +92,10 @@ abstract final class WorkBoundary {
|
||||
|
||||
/// True when this order is finished or withdrawn, whatever screen it was on.
|
||||
///
|
||||
/// [StopStatus.picked] is terminal on a line that ends at the hub and is the
|
||||
/// middle of the morning on one that does not — so it is asked of the line,
|
||||
/// through the same knob everything else here reads. See
|
||||
/// [ServiceProfile.endsAtHub].
|
||||
/// [StopStatus.picked] is terminal for some orders and the middle of the
|
||||
/// morning for others, and **which one is a property of the order, not of the
|
||||
/// line**. It is asked of [NextLegResolver], which reads the backend's own
|
||||
/// routing answer. See the note on the picked branch below.
|
||||
///
|
||||
/// ── Skipped closes, and why it has to close *here* ──
|
||||
///
|
||||
@@ -128,6 +129,9 @@ abstract final class WorkBoundary {
|
||||
static bool isClosed(
|
||||
Map<String, dynamic> stop, {
|
||||
Set<String> closedIds = const <String>{},
|
||||
|
||||
/// `next_action` recorded at each order's pivot — see [NextLegResolver].
|
||||
Map<String, String> pivotActions = const <String, String>{},
|
||||
}) {
|
||||
if (closedIds.isNotEmpty && closedIds.contains(MilkRun.idOf(stop))) {
|
||||
return true;
|
||||
@@ -138,7 +142,31 @@ abstract final class WorkBoundary {
|
||||
// rider walked away from and one the office called off are different
|
||||
// records, and Activity slices them apart.
|
||||
if (status.isSkipped) return true;
|
||||
return status.isPicked && ServiceProfile.active.endsAtHub;
|
||||
|
||||
// ── After the pickup, ownership is a question about the ORDER ──
|
||||
//
|
||||
// This read `status.isPicked && ServiceProfile.active.endsAtHub`, and that
|
||||
// is a line-level constant deciding a per-order fact. On logistics
|
||||
// `endsAtHub` is always true, so **every collected parcel was closed at the
|
||||
// pickup** — including the hyperlocal ones the pivot had just released
|
||||
// `Out_for_Delivery` into this rider's own hands. He walked away from the
|
||||
// sender's door carrying a parcel his app had already filed as finished.
|
||||
//
|
||||
// The backend decides where a parcel goes next and always has; the app now
|
||||
// asks. [NextLeg.closed] is the only answer that retires the stop —
|
||||
// `customer` and `hub` are both journeys the rider still owes, and
|
||||
// `unknown` is a parcel in his bag whose destination has not been named,
|
||||
// which must never be silently written off. See [NextLegResolver] for the
|
||||
// precedence between the pivot, the row and the local record.
|
||||
//
|
||||
// [ServiceProfile.endsAtHub] survives as a line-level capability hint — it
|
||||
// still tells the route card whether the day ends at a depot — but it no
|
||||
// longer decides whether one order is finished.
|
||||
if (!status.isPicked && !status.isDeliveryLeg) return false;
|
||||
return NextLegResolver.resolve(
|
||||
stop,
|
||||
pivotAction: pivotActions[MilkRun.idOf(stop)] ?? '',
|
||||
).isClosed;
|
||||
}
|
||||
|
||||
/// Which screen owns this order.
|
||||
@@ -154,8 +182,42 @@ abstract final class WorkBoundary {
|
||||
/// Orders written off today — delivered, cancelled or skipped — read back
|
||||
/// from the completed and skipped stores. See [isClosed].
|
||||
Set<String> closedIds = const <String>{},
|
||||
|
||||
/// `next_action` recorded at each order's pivot — see [NextLegResolver].
|
||||
Map<String, String> pivotActions = const <String, String>{},
|
||||
}) {
|
||||
if (isClosed(stop, closedIds: closedIds)) return WorkDomain.closed;
|
||||
if (isClosed(stop, closedIds: closedIds, pivotActions: pivotActions)) {
|
||||
return WorkDomain.closed;
|
||||
}
|
||||
|
||||
// ── A collected parcel is the work tab's, whatever the line says ──
|
||||
//
|
||||
// Below [isClosed] and above the handoff knob, deliberately. Once the
|
||||
// backend confirms the collection the parcel is physically the rider's, and
|
||||
// "in my hands, still owed a journey" is the definition of the delivery
|
||||
// domain on every line. Reading it here rather than from the per-line
|
||||
// handoff point is what lets a hyperlocal logistics parcel reach Deliveries
|
||||
// at all — its own line hands off at *acceptance*, which says nothing about
|
||||
// custody.
|
||||
//
|
||||
// ── Gated on the collection having happened ──
|
||||
//
|
||||
// [NextLegResolver] answers [NextLeg.unknown] for any row it cannot place,
|
||||
// and an uncollected booking is one of those: a pending assignment carries
|
||||
// no consignment state and no pivot record, exactly like a collected parcel
|
||||
// the server has not routed yet. Asking without this gate handed every
|
||||
// pending stop to Deliveries — the dual-ownership failure in reverse.
|
||||
//
|
||||
// "Is it in his hands?" is [pickupComplete]'s question and it has a real
|
||||
// answer: the backend's own status, or the collected record written after
|
||||
// the pivot came back OK. Only then is there a leg to resolve.
|
||||
if (pickupComplete(stop, collectedIds: collectedIds) &&
|
||||
NextLegResolver.resolve(
|
||||
stop,
|
||||
pivotAction: pivotActions[MilkRun.idOf(stop)] ?? '',
|
||||
).isInCustody) {
|
||||
return WorkDomain.delivery;
|
||||
}
|
||||
|
||||
final handedOver = switch (ServiceProfile.active.handoffAt) {
|
||||
HandoffPoint.collected => pickupComplete(
|
||||
@@ -184,6 +246,7 @@ abstract final class WorkBoundary {
|
||||
Set<String> collectedIds = const <String>{},
|
||||
Set<String> acceptedIds = const <String>{},
|
||||
Set<String> closedIds = const <String>{},
|
||||
Map<String, String> pivotActions = const <String, String>{},
|
||||
}) => [
|
||||
for (final stop in day)
|
||||
if (domainOf(
|
||||
@@ -191,6 +254,7 @@ abstract final class WorkBoundary {
|
||||
collectedIds: collectedIds,
|
||||
acceptedIds: acceptedIds,
|
||||
closedIds: closedIds,
|
||||
pivotActions: pivotActions,
|
||||
) ==
|
||||
WorkDomain.delivery)
|
||||
stop,
|
||||
@@ -202,6 +266,7 @@ abstract final class WorkBoundary {
|
||||
Set<String> collectedIds = const <String>{},
|
||||
Set<String> acceptedIds = const <String>{},
|
||||
Set<String> closedIds = const <String>{},
|
||||
Map<String, String> pivotActions = const <String, String>{},
|
||||
}) => [
|
||||
for (final stop in day)
|
||||
if (domainOf(
|
||||
@@ -209,6 +274,7 @@ abstract final class WorkBoundary {
|
||||
collectedIds: collectedIds,
|
||||
acceptedIds: acceptedIds,
|
||||
closedIds: closedIds,
|
||||
pivotActions: pivotActions,
|
||||
) ==
|
||||
WorkDomain.pickup)
|
||||
stop,
|
||||
|
||||
Reference in New Issue
Block a user