130 lines
4.3 KiB
Go
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
|
|
},
|
|
}
|
|
}
|