agent
This commit is contained in:
129
services/tools/deliveryprogress.go
Normal file
129
services/tools/deliveryprogress.go
Normal file
@@ -0,0 +1,129 @@
|
||||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user