276 lines
10 KiB
Go
276 lines
10 KiB
Go
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]
|
|
}
|