WFDASH-WEBAPP/docs/sops/approver-supervisor.md
Dave aed3f5c861 feat: implement discount functionality in POS system
Added support for discounts in the POS checkout process, allowing operators to apply either a fixed amount or a percentage off the gross total. Updated the state management to include discount details and modified the receipt and cart rendering to display pre- and post-discount values. Enhanced the checkout API to handle discount inputs and ensure proper calculations. Updated relevant tests to cover new discount logic.
2026-09-03 12:19:21 +02:00

439 lines
18 KiB
Markdown

# Approver / Supervisor — SOP
**Owns:** the approvals queue, pricing/markup policy, escalations
from Quote and Order Operators, weekly KPI reviews, and the
health of the system overall.
**Tools:** `/quotes/approvals`, all operator routes (read),
`/dashboard`, `/customers`, `/suppliers`, `/settings`,
`/api/v1/...` admin endpoints, the daily summary call.
**Handoffs:**
- **From Quote Operator** → quote in `approval_pending`.
- **To Quote Operator** → approved / rejected / change-requested.
- **From Order Operator** → escalations (cancellation, refund,
discount > 15%, supplier issue).
- **To Order Operator** → decision + instruction.
- **From Collection Driver** → escalations (refusal, damage
dispute, customer relationship issue).
- **To everyone** → policy updates, training, weekly review.
---
## 1. Daily / Weekly Checklist
### Every working day (start of shift, 08:00 SAST)
- [ ] **Open `/quotes/approvals`.** Clear the queue. Anything
older than 4 working hours needs a decision today.
- [ ] **Open `/dashboard`.** Quick scan:
- **Approval Rate** gauge — should be > 70% on a healthy
pipeline.
- **Quote Pipeline** donut — too much in `new` for >2 days
is a Quote Operator capacity problem.
- **Follow-ups Due** — count should be trending down.
- **Concentric rings** (quote pipeline depth) — if the
"Approved" ring is growing while the "Sent" ring is
shrinking, the email send flow has a problem.
- [ ] **Check `#quote-ops` and `#order-ops`** channels. Triage
any escalations posted overnight.
- [ ] **Skim `/orders` — Cancelled tab.** A spike in
cancellations = supplier problem or pricing problem.
Investigate.
### Every working day (mid-day, 13:00 SAST)
- [ ] **Re-check `/quotes/approvals`.** Anything still in
`approval_pending` from this morning is now a process bug.
- [ ] **Triage any pending escalations.** Quote Operators and
Order Operators should be unblocked by lunch.
- [ ] **Review one random operator action** (audit). Pick an
order or quote at random, walk through the action log.
Calibrates your understanding of the team's actual work.
### Every working day (end of shift, 17:00 SAST)
- [ ] **Clear the day's escalations.** Every escalation should
have either a decision or a clear next step.
- [ ] **Review today's KPI snapshot.** Any operator trending
red? Send them a 1-line note (not a formal message — keep
it human).
### Every Monday morning (90 min block)
- [ ] **Pull the weekly KPI one-pager** for each Quote and
Order Operator (and the Collection Driver's app-export).
- [ ] **Hold the weekly standup** (15 min):
- Each operator: 1 number (their worst KPI) + 1 win.
- You: 1 policy reminder + 1 system change coming this week.
- [ ] **Update pricing policy** if any of these moved:
- Supplier price list changes (cost-side).
- Competitor pricing (market-side).
- Currency moves (ZAR volatility).
- A new strategic account needs a standing discount.
### Every Friday afternoon
- [ ] **Write the 1-page weekly summary.** Distribution: owner
(you), the 4 operators, the invoicing clerk (if separate),
and the owner / MD if you have one. Sections:
- **Pipeline health** (counts by status, vs last week).
- **Top 3 wins** (one per operator, named).
- **Top 3 issues** (with an owner + deadline).
- **1 thing we changed** (a process, a policy, a system tweak).
- **Next week's risks.**
- [ ] **Forecast cash collection** for the next 30 days. Pull
`delivered` orders not yet `invoiced`, sum their
`selling_price`. This is the cash-in number the owner wants.
### Every month (1st, 60 min)
- [ ] **Monthly 1-on-1 with each operator.** 30 min each.
Agenda (use the 1-page review you wrote):
- Their KPIs vs target.
- Their 30-day improvement action.
- What they need from you (training, tools, headcount).
- What they want to keep doing.
- [ ] **Catalogue coverage review.** What % of quote items
this month were matched in the catalogue? If < 50%, the
Pricelist Curator needs to upload more supplier lists.
- [ ] **Supplier performance review.** Late deliveries,
cost-mismatch rate, damage rate. Note any supplier that
should be replaced or have terms renegotiated.
### Every quarter
- [ ] **Strategic review.** 2-hour session with the owner.
Topics: revenue trend, margin trend, customer concentration
risk, supplier concentration risk, system gaps.
- [ ] **Operator tier reviews.** Who's eligible for markup-
authority / trusted-buyer / senior-driver tier?
- [ ] **System roadmap input.** What 1-2 features would
unlock the most operator capacity?
---
## 2. Step-by-Step Procedures
### 2.1 Approve a quote
**When:** a quote lands in `approval_pending`.
1. Open `/quotes/approvals`. The queue is sorted by age,
oldest first.
2. Click a quote. Review:
- **Customer + rep** — known account? Standing discount?
- **All lines priced** (every line has `selling_price > 0`).
- **Markup** — does the markup meet the policy for this
category / customer?
- **Validity** — is the validity date realistic?
- **Supplier** — do we have a healthy relationship with
this supplier? Any open POs with them?
3. **Decide:**
- **Approve** → click **Approve**. Status flips to
`approved`. The Quote Operator can now send.
- **Request changes** → add a comment in the change-
request field. Status stays in `approval_pending` (or
reverts to `processed` if it was a pricing issue). The
Quote Operator re-submits.
- **Reject** → add a reason. Status flips to `rejected`.
The Quote Operator may revise or close the quote.
4. **Log the decision in `quote.notes`** (auto-stamped by
the system, but add context if it's non-obvious — e.g.
"Approved at 18% markup, customer's standing discount
from June 2026 contract").
**Don't:**
- Don't approve without reading the full quote. A 30-second
review misses the wrong SKU, the wrong qty, the wrong
customer.
- Don't reject without a reason. "No" is demoralising;
"No, because the JEI prices are below cost" is a teachable
moment.
### 2.2 Set / change a pricing policy
**When:** cost or market conditions change.
1. **Identify the affected scope.** Is it a category (e.g.
"all FASTENERS"), a customer (e.g. "Account XYZ"), or a
supplier (e.g. "all JEI lines")?
2. **Pull the data.** What is the current avg markup for
that scope? What are competitors charging? What's the
supplier cost trend?
3. **Propose the new policy.** Document:
- Scope (which customers / categories / SKUs).
- New markup range.
- Effective date.
- Reason (market move, supplier cost, strategic account).
4. **Communicate.** Post in `#quote-ops`, mention in the
Monday standup, and pin in `/settings` (if there's a
pricing-policy field there).
5. **Audit after 2 weeks.** Pull the avg markup for the
scope — is the team applying the new policy? If not,
retrain.
### 2.3 Handle an escalation from a Quote Operator
**When:** a Quote Operator pings you with a question
(markup override, customer relationship, stuck item).
1. **Read the full context** before responding. Don't
respond to a 1-line ping with a 1-line answer.
2. **Categorise:**
- **Pricing** → see §2.2. Likely a one-off override.
- **Customer relationship** → call the customer yourself
if strategic; delegate back if not.
- **System / data** → the system has a gap. File a
feature request in `src/app/api/mcp/FEATURE-REQUESTS.md`
(or whatever the current backlog file is).
- **Process** → the SOP is unclear. Update the relevant
SOP (and tell the team).
3. **Respond with a decision + a why.** The operator needs
to learn from the decision, not just execute it.
4. **Follow up next day.** Verify the decision was applied.
### 2.4 Handle an escalation from an Order Operator
**When:** an Order Operator pings you with cancellation,
refund, discount > 15%, supplier dispute, customer
complaint.
1. **Same triage as §2.3**, but the stakes are usually
higher (real money, real relationships).
2. **For customer complaints** — get the customer's side of
the story first. Don't take the operator's word at face
value; don't take the customer's at face value either.
3. **For supplier disputes** — pull the PO + delivery
evidence (photos, signed notes). Make the call based on
the contract, not the relationship.
4. **Document the decision** in the order's `notes` and
your private escalation log.
### 2.5 Run a weekly KPI review
**When:** every Monday.
1. **Pull the one-pager.** Each operator has 4-6 KPIs. The
report highlights who's red, who's yellow, who's green.
2. **For each red operator:**
- Look at the underlying data. Is it a process issue,
a capacity issue, or a skill issue?
- **Process** → fix the SOP, retrain.
- **Capacity** → redistribute the load or accept the
miss.
- **Skill** → 1-on-1 coaching, shadow a senior operator.
3. **For each green operator:**
- Find the win. Mention it in the standup.
- If they're consistently green, consider the tier
bump.
4. **Write the 1-page weekly summary.** (See §1 Friday.)
### 2.6 Audit a random operator action
**When:** mid-day, when you have 10 minutes.
1. Pick a random order or quote from the last 24 hours.
2. Walk the action log: who touched it, when, what
changed.
3. **Look for:**
- Status transitions that don't match the SOP (e.g.
`pending` → `delivered` skipping the intermediate
states).
- Pricing changes without a `pricing_notes` breadcrumb.
- Items stuck in a state longer than the SLA.
4. **If you find a real issue:** message the operator
privately, not in public. Coaching, not blame.
5. **If you find a SOP gap:** update the SOP.
---
## 3. Decision Tree
### 3.1 Should I approve this discount?
```
What's the customer's standing discount (per the customer
record / contract)?
├── Matches → approve at face value
└── Doesn't match
├── Is the difference small (< 3%)? → approve, log the
│ reason (e.g.
│ "volume uplift
│ from Q3 contract").
├── Is the difference medium (3-10%)? → approve if
│ the customer
│ is strategic
│ OR the deal
│ is a one-off
│ (large order,
│ end-of-line
│ stock, etc).
│ Otherwise,
│ ask the
│ operator to
│ split the
│ margin with
│ the customer
│ themselves.
└── Is the difference large (> 10%)? → talk to the
customer
yourself.
Strategic
pricing is a
relationship
call, not a
quote.
```
### 3.2 Supplier wants to renegotiate terms
```
Is the supplier a strategic dependency (> 20% of our spend)?
├── Yes → call the supplier yourself. Don't delegate.
│ Renegotiation is a relationship call.
└── No
├── Will the new terms save us money or improve
│ service? → accept, log in the supplier record.
└── Will they cost more or worsen service? → decline,
keep the
relationship
professional
(don't burn
the bridge).
```
### 3.3 Customer escalates a quality issue
```
Is the issue real (defect, wrong item, damaged in transit)?
├── Yes
├── Was the issue caused by us (wrong pick, late
│ delivery, damaged in our care)?
│ ├── Yes → own it. Replace or refund, no argument.
│ │ Don't ask the customer to prove the
│ │ damage.
│ └── No (supplier defect, customer mishandling)
│ ├── Defect is our supplier's → call them, get
│ │ a credit or
│ │ replacement, pass
│ │ it on.
│ └── Customer mishandling → politely decline.
│ Offer a goodwill
│ discount at your
│ discretion.
└── What's the cost of fixing vs losing the customer?
├── Customer is strategic → fix, no matter the
│ cost. Relationships
│ > margins.
└── Customer is transactional → fix only if it's
cheaper than
losing them. Most
one-off customers
aren't worth a
free replacement.
```
### 3.4 An operator is consistently missing a KPI
```
How long have they been missing it?
├── < 2 weeks → noise. Mention in the 1-on-1, move on.
├── 2-4 weeks → pattern. Diagnose:
│ ├── Process gap (unclear SOP) → update the SOP,
│ │ retrain.
│ ├── Capacity gap (too much load) → redistribute.
│ └── Skill gap (doesn't know how) → pair with a
│ senior operator for
│ a week.
└── > 4 weeks → performance issue. Formal conversation.
Set a 30-day improvement target with consequences
if missed.
```
### 3.5 A quote is in `approval_pending` for > 1 working day
```
Who submitted it?
├── Quote Operator on shift → ping them directly. "Why is
│ this still in my queue?"
└── Quote Operator off shift / on leave → reassign to
another operator
OR approve it
yourself if
straightforward.
```
### 3.6 The system is misbehaving (data loss, wrong status, etc.)
```
Is the issue isolated to one record?
├── Yes → fix the record directly via the admin tools or
│ `pb-helpers.ts` / service layer. Document the
│ fix.
└── No (multiple records, or a pattern)
├── Stop the workflow that's causing the damage.
│ (e.g. pause the n8n email intake if it's creating
│ bad quotes.)
├── Page the dev. Don't try to fix the code yourself
│ unless you ARE the dev.
├── Communicate to the team: "stop doing X until
│ further notice."
└── After the fix: write up the post-mortem. What
happened, what we did, what we'll do to prevent
it. Share with the team.
```
### 3.7 Strategic account wants terms we don't usually offer
```
What's the customer's annual spend with us?
├── Top 10% → call the owner / MD. Strategic accounts
│ can have bespoke terms; it's a relationship
│ call.
├── 10-25% → decide yourself. Document the terms in the
│ customer record, in a contract if material.
└── Bottom 75% → decline. Standardise. Bespoke terms
for non-strategic accounts erode
margin and create admin overhead.
```
---
## 4. KPIs
The supervisor's KPIs are different from the operators' —
they're about the team's health, not the supervisor's own
output.
| KPI | Target | How it's measured |
|---|---|---|
| **Approval turnaround** | < 4 working hours median | `approved_at - submitted_at` on `quotes/approvals` |
| **Escalation response time** | < 2 working hours | `decision_at - escalation_at` on the escalation log |
| **Team KPI attainment** | > 80% of operator KPIs at target | weekly one-pager summary |
| **Quote → order conversion** | > 35% trailing 90 days | orders / emailed quotes |
| **On-time delivery rate** | > 90% trailing 30 days | orders delivered by `delivery_date` / total delivered |
| **Catalogue coverage** | > 60% of quote items matched | sprint items with `matched_price_list_id` / total sprint items |
| **Supplier cost-mismatch rate** | < 3% trailing 30 days | `cost_mismatch` verdicts / total receives |
| **Customer concentration risk** | top customer < 30% of revenue | revenue by customer over trailing 90 days |
| **System uptime** | > 99% trailing 30 days | uptime of WFDASH + PB + n8n (monitor externally) |
| **Team tenure** | trending up | months since each operator's start date |
### How the supervisor's KPIs feed into YOUR review
- **Weekly**: the owner / MD sees the weekly summary. Your
team KPIs are the headline.
- **Monthly**: 1-on-1 with the owner / MD. Focus on
strategic accounts, concentration risk, and system gaps.
- **Quarterly**: strategic review. This is where you push
for the headcount / tools / process changes the team
needs.
### What the system does NOT measure (yet)
- Operator morale (no internal NPS yet)
- Customer satisfaction (no external NPS yet)
- Operator skill growth (no per-operator skill matrix yet)
If a KPI is unfair, raise it. The owner can adjust
targets, but the metric stays.