Files
backend_fiesta/services/tools/deliveryprogress.go
2026-09-23 17:26:13 +05:30

130 lines
4.3 KiB
Go

package tools
import (
"context"
"fmt"
"sort"
"strings"
"nearle/models"
)
// "What is out for delivery?"
//
// The question the order table cannot answer. Fiesta mirrors only three delivery
// statuses onto the order — pending, delivered, cancelled — so an order row
// reads "pending" from the moment a rider is assigned until the moment the job
// completes. A rider standing at the customer's door and a job nobody has been
// told about are the same row there.
//
// The delivery row carries the real stage, so this counts from that instead.
// It is the same correction `orderProgress.ts` makes in the console, applied at
// the other end.
//
// ── Counts, not a list ─────────────────────────────────────────────────────
//
// "What is out for delivery?" is a question about how much, not which. Forty
// rows summarised by a model becomes a sentence nobody can check; a stage with a
// count against it is a number a dispatcher can act on. The list is one click
// away and the answer says where.
// The delivery ladder, in the order a job walks it.
//
// Fixed order rather than whatever the map iterates to: a person reading
// "picked 3, accepted 8, arrived 1" has to reassemble the journey in their head
// every time.
var deliveryStages = []struct {
key string
label string
live bool
}{
{"pending", "Not yet accepted", true},
{"accepted", "Accepted", true},
{"arrived", "At the shop", true},
{"picked", "Picked up", true},
{"active", "On the way", true},
{"skipped", "Nobody answered", false},
{"rejected", "Declined by the rider", false},
{"delivered", "Delivered", false},
{"cancelled", "Cancelled", false},
}
// StageCount is one rung of the ladder.
type StageCount struct {
Stage string `json:"stage"`
Count int `json:"count"`
// True while somebody is still carrying the job. `skipped` and `rejected`
// are stopped rather than finished, and both need a person — so neither is
// live, and neither is done.
Live bool `json:"in_progress"`
}
// DeliveryProgress builds the tool.
func DeliveryProgress(deliveries DeliveryReader) Tool {
return Tool{
Name: "delivery_progress",
Description: "How many deliveries are at each stage right now — waiting to be accepted, picked up, on the way, delivered. " +
"Use for questions about what is out for delivery, how deliveries are going, or how many are still in progress. " +
"The order list cannot answer this: an order row says 'pending' for the whole journey.",
Scope: ScopeRead,
Schema: Schema{},
Handler: func(_ context.Context, req Request) (Result, error) {
rows := deliveries.GetDeliveries(models.DeliveryQuery{
Tenantid: req.Caller.Tenantid,
Locationid: req.Caller.Locationid,
Pagesize: 500,
Pageno: 1,
})
counts := map[string]int{}
var unknown []string
for _, row := range rows {
stage := strings.ToLower(strings.TrimSpace(row.Orderstatus))
if stage == "" {
stage = "pending"
}
counts[stage]++
}
out := make([]StageCount, 0, len(deliveryStages))
live, finished := 0, 0
for _, stage := range deliveryStages {
count := counts[stage.key]
delete(counts, stage.key)
// Stages with nothing in them are left out. A ladder of zeroes
// buries the two rungs that have anything on them.
if count == 0 {
continue
}
out = append(out, StageCount{Stage: stage.label, Count: count, Live: stage.live})
if stage.live {
live += count
} else {
finished += count
}
}
// A status the ladder has never heard of. Reported rather than
// dropped: a stage nobody counted is how a whole category of work
// goes missing from a board that looks complete.
for stage, count := range counts {
unknown = append(unknown, fmt.Sprintf("%s (%d)", stage, count))
}
sort.Strings(unknown)
result := Result{
Rows: out,
Count: live,
Source: "/admin/dispatch",
Scope: scopeWords(req.Caller),
Note: fmt.Sprintf("%d deliveries still in progress, %d finished or stopped. The count is jobs in progress.",
live, finished),
}
if len(unknown) > 0 {
result.Note += " Statuses this console does not recognise: " + strings.Join(unknown, ", ") + "."
}
return result, nil
},
}
}