package constants // Miler Availability Statuses const ( MilerOffline = "Offline" MilerAvailable = "Available" MilerAssigned = "Assigned" MilerOnPickup = "On_Pickup" MilerAtCustomer = "At_Customer" MilerPickedUp = "Picked_Up" MilerOnDelivery = "On_Delivery" MilerBreak = "Break" MilerBlocked = "Blocked" ) // MilerWorkingStatuses are the states in which a miler may be GIVEN more work. // // A miler carrying an order is still a miler on the road. Courier rounds are // multi-stop by nature, and every candidate query used to test // `availabilitystatus = 'Available'`, which treats the first booking as a // lock: the moment a rider took one order they vanished from every assignment // path, and the per-rider load caps that exist precisely to govern this never // got a chance to run. Whether a rider can take another job is a question // about how much they are already carrying — counted from their open // assignments — not about whether they are carrying anything at all. // // Offline, Break and Blocked are the only states that take a miler out. They // are excluded by naming the ones that are in, so a status added later is // off-duty until someone decides otherwise. var MilerWorkingStatuses = []string{ MilerAvailable, MilerAssigned, MilerOnPickup, MilerAtCustomer, MilerPickedUp, MilerOnDelivery, } // MilerCanTakeWork reports whether a miler in this state may receive another // booking. Load is capped separately, by counting open assignments. func MilerCanTakeWork(status string) bool { for _, s := range MilerWorkingStatuses { if s == status { return true } } return false } // Booking sources — where a booking originated. Left as their stored literals: // "CRM_Console" predates the outward express rename and is an existing DB value. const ( BookingSourceCustomerApp = "Customer_App" BookingSourceExpress = "CRM_Console" ) // App (B2C) Customer Statuses const ( CustomerStatusActive = "Active" CustomerStatusBlocked = "Blocked" CustomerStatusDeleted = "Deleted" ) // Booking Statuses const ( BookingPendingPickup = "Pending_Pickup" // customer requested pickup, delivery details not yet known BookingCreated = "Created" BookingMilerAssigned = "Miler_Assigned" BookingPickupScheduled = "Pickup_Scheduled" BookingArrivedAtPickup = "Arrived_At_Pickup" // miler is at the pickup point, parcel not yet collected BookingPickedUp = "Picked_Up" BookingConvertedConsignment = "Converted_To_Consignment" BookingCancelled = "Cancelled" ) // Consignment Statuses const ( ConsignmentCreated = "Created" ConsignmentInwardedAtHub = "Inwarded_at_Hub" // ConsignmentCollectedByMiler is the intermediate state for a hyperlocal // parcel: the miler has collected it but has NOT yet started the final-mile // run. It sits between pickup and Out_for_Delivery so the console can tell // "collected, waiting to leave" apart from "actively delivering" — before // this existed a hyperlocal pickup jumped straight to Out_for_Delivery and // looked active the instant it was collected. StartDelivery moves it on. ConsignmentCollectedByMiler = "Collected_By_Miler" ConsignmentTripsheetLoaded = "Tripsheet_Loaded" ConsignmentInTransit = "In_Transit" ConsignmentOutForDelivery = "Out_for_Delivery" ConsignmentDelivered = "Delivered" ConsignmentRTOInitiated = "RTO_Initiated" ConsignmentReturnedToSender = "Returned_to_Sender" ConsignmentMissing = "Missing" ConsignmentDamaged = "Damaged" ) // Machine-readable error codes returned to the miler app in the "code" field of // 4xx responses, so the client can branch on a stable identifier instead of // parsing human-readable messages. Add here, never inline. const ( ErrInvalidInput = "INVALID_INPUT" ErrBookingNotFound = "BOOKING_NOT_FOUND" ErrBookingNotAssigned = "BOOKING_NOT_ASSIGNED" ErrConsignmentNotFound = "CONSIGNMENT_NOT_FOUND" ErrConsignmentNotAssigned = "CONSIGNMENT_NOT_ASSIGNED" ErrInvalidState = "INVALID_STATE" // action not allowed from the entity's current status ErrAlreadyPickedUp = "ALREADY_PICKED_UP" // pre-pickup action attempted after pickup ErrOtpRequired = "OTP_REQUIRED" ErrOtpInvalid = "OTP_INVALID" ErrIdempotencyInProgress = "IDEMPOTENCY_IN_PROGRESS" // an identical keyed request is still running ErrEmailInUse = "EMAIL_IN_USE" ErrHubNotFound = "HUB_NOT_FOUND" // hub_id on a handover does not resolve to an active base ErrHubRequired = "HUB_REQUIRED" // handover attempted with no base to hand over to ) // Pickup source types — what kind of place a booking is collected FROM. Sent // on every miler booking row as pickup_source_type so the rider app can title a // stop correctly instead of guessing from the source name, the pincode or the // rider's own base. "customer" is a real value, never an omission: a front-door // pickup has no configured location id, and "no location because it is a front // door" must be distinguishable from "no location because nobody filled it in". // // The rider app renders "hub" as Base — the wire value stays hub. const ( PickupSourceHub = "hub" PickupSourceCustomer = "customer" PickupSourceMerchant = "merchant" PickupSourceStore = "store" ) // Next actions — what the rider does next with a parcel. Returned by // pickup-complete and, so a poll or a cold restart can rebuild the leg without // a local cache, on every GET /miler/bookings row. Consignment status alone // cannot carry this: a hub-routed parcel and a freshly-collected hyperlocal one // can both sit on Created. const ( NextActionPickup = "pickup" // not collected yet — the stop is the pickup NextActionStartDelivery = "start_delivery" // collected, hyperlocal, not yet out for delivery NextActionDeliver = "deliver" // carry it to the receiver NextActionInwardAtHub = "inward_at_hub" // carry it to a base and hand it over NextActionHandedToHub = "handed_to_hub" // already inwarded at the base — nothing left for this rider NextActionNone = "none" // terminal (delivered, cancelled, returned) ) // Payment Modes const ( PaymentModeCash = "Cash" PaymentModeUPI = "UPI" PaymentModeCard = "Card" PaymentModeWallet = "Wallet" ) // Payment Statuses const ( PaymentStatusPending = "Pending" PaymentStatusPaid = "Paid" PaymentStatusFailed = "Failed" PaymentStatusRefunded = "Refunded" ) // Assignment Statuses const ( AssignmentAssigned = "Assigned" AssignmentAccepted = "Accepted" AssignmentRejected = "Rejected" AssignmentReassigned = "Reassigned" AssignmentCompleted = "Completed" AssignmentCancelled = "Cancelled" ) // Tripsheet Statuses const ( TripsheetDraft = "Draft" TripsheetReady = "Ready" TripsheetDispatched = "Dispatched" TripsheetArrived = "Arrived" TripsheetCancelled = "Cancelled" ) // Scan Statuses const ( ScanPending = "Pending" ScanLoaded = "Loaded" ScanUnloaded = "Unloaded" ScanDiscrepancy = "Discrepancy" ) // Exception Types const ( ExceptionLost = "Lost" ExceptionDamaged = "Damaged" ExceptionMisrouted = "Misrouted" ExceptionReceiverRefused = "Receiver_Refused" ExceptionMissingContents = "Missing_Contents" ExceptionUndeliverable = "Undeliverable" ) // Exception Statuses const ( ExceptionOpen = "Open" ExceptionUnderInvestigation = "Under_Investigation" ExceptionResolved = "Resolved" ExceptionClosed = "Closed" ) // Customer-app stages. Nine operational stages, spelt exactly as the customer // client parses them: lowercase snake_case, on the wire verbatim. The client // rolls these up into seven milestones itself and falls back to "booked" on an // unknown key, silently — so adding a value here without an app release makes a // parcel look un-started. Never rename one; add and coordinate. // // Stages 0-5 belong to the booking. Stages 6-8 belong to each order and may // differ between destinations of the same booking. const ( CxStageBooked = "booked" // 0 — pickup requested CxStageAssigned = "assigned" // 1 — a miler accepted it CxStageOnTheWay = "on_the_way" // 2 — rider en route, distance/ETA live CxStageArrived = "arrived" // 3 — rider at the door; LAST cancellable stage CxStagePickedUp = "picked_up" // 4 — weighed, photographed, price settled CxStageOrderCreated = "order_created" // 5 — one tracking number minted per destination CxStageInTransit = "in_transit" // 6 — per order from here on CxStageOutForDelivery = "out_for_delivery" // 7 — delivery agent carrying it CxStageDelivered = "delivered" // 8 — handed over ) // Customer-facing booking status. Derived from the stage but sent explicitly, // because a client that has to infer it will eventually infer it differently. const ( CxStatusActive = "active" CxStatusCompleted = "completed" CxStatusCancelled = "cancelled" ) // Who caused a stage transition. Recorded on every bookingstageevents row: the // customer timeline is derived from that table, so it has to be real, and a // cancellation the customer did not make is unexplainable without this. const ( CxActorMiler = "miler" CxActorOps = "ops" CxActorCustomer = "customer" CxActorSystem = "system" ) // CxStageOrder is the rank of each stage, used to decide whether a transition // moves forward and whether cancellation is still open. Cancellation closes // after arrived, so anything at or past picked_up is refused. var CxStageOrder = map[string]int{ CxStageBooked: 0, CxStageAssigned: 1, CxStageOnTheWay: 2, CxStageArrived: 3, CxStagePickedUp: 4, CxStageOrderCreated: 5, CxStageInTransit: 6, CxStageOutForDelivery: 7, CxStageDelivered: 8, } // CxStageRank returns the rank of a stage, or -1 when the stage is unknown or // empty. A booking written before this surface existed has no stage at all, // and -1 keeps it strictly behind every real stage rather than tying with // "booked". func CxStageRank(stage string) int { if r, ok := CxStageOrder[stage]; ok { return r } return -1 } // CxCancellable reports whether a booking at this stage may still be cancelled. // The UI mirrors this to hide the button, but the server re-checks on the // cancel call — the button state is a hint, never the authority. func CxCancellable(stage string) bool { return CxStageRank(stage) <= CxStageOrder[CxStageArrived] }