← automation
sherpa-api#456 Done · In review ⏱ 37m 33s
Outlet catalog + per-product outlet assignment
⎇ auto/api-456-outlet-catalog-per-product-outle
Portal localhost:3001Embed localhost:3101API localhost:4001
🔍 iterative-review ✓ (main-pass) 🧪 test:api failed 235 passed · 5 failed · 0 flaky · 12 skipped · report 🧑‍🔬 QA ✅ PASS

Feedback

Pull requests

loading…

QA evaluation

✅ PASS

PASS. All four acceptance criteria were verified by driving the real sherpa-portal UI (headless Chromium, minted storageState against portal_url) - not via API. Outlets CRUD works end-to-end (created 3 outlets through the form, listed, searched, with archive support); a single product was assigned two specific outlets via the product-detail Outlets section with no product duplication; Policy Management shows the Outlet column and a working outlet filter; and Financials renders the 'Premium by Outlet' rollup with the org total and the required '(unattributed historical sales)' row on real data. The ticket's dedicated API suite (09-outlets-and-attribution.spec.ts) passes 9/9 with no failures (run scoped since tests_status was unknown). The only data-dependent gap - no named-outlet revenue/policy rows yet - stems from there being no outlet-attributed sales (the buyer-funnel selector is explicitly out of scope), not from any missing UI; every screen, column, filter, and rollup the ticket requires genuinely exists and functions.

4/4 acceptance criteria passed
  • Create several outlets under one org; they appear in the Outlets listDrove portal UI at /outlets. Used the 'Create outlet' button + form to create 3 outlets (QE Nordbank Oslo, QE Velora Bergen, QE Voltkrone Web) with unique external refs. All three returned 'Outlet created successfully' toasts and appear in the org-scoped list table (Name, External ref, Status badge=Active, Created date, Actions). Search-by-ref returned exactly the matching outlet. Nav entry 'Outlets' present under Sherpa Plus. Screenshot eval-1-outlets-created.png.
  • Assign multiple outlets to one product (no product duplication)On a single product detail page (Overview tab) the 'Outlets' section renders ('Choose which selling points can sell this product. No product duplication required.'). Toggled OFF 'Available to all outlets', selected 2 outlets via the multi-select, clicked 'Save outlets' -> 'Outlet availability updated' toast. UI shows '2 item(s) selected' and two assignment badges (QE Nordbank Oslo, QE Velora Bergen). One product, two outlets, no duplicate product. Screenshot eval-2-product-outlets-assigned.png.
  • Policy Management — Outlet column + filter by outlet/policy-management shows an 'Outlet' column in the policy table (data-testid policy-outlet-cell) and an 'Outlet' filter dropdown alongside Type/Country/Status. The dropdown opens with a search box, 174 org outlet options, and an Apply button; applying an outlet re-queries the backend (param outletId) and refreshes the table. Current cells render '-' because existing policies have no outlet_id (no outlet-attributed sales yet), which is the correct rendering for unattributed policies. Screenshots eval-3-policy-outlet-filter.png, eval-3a-policy-outlet-column.png.
  • Financials — revenue rolled up per outlet/financials?tab=premium-by-outlet renders the 'Premium by Outlet' screen (sub-nav 'By Outlet') wired to GET /v1/plus/financials/revenue/by-outlet. Table columns: Outlet, External ref, Total Invoiced, Refunds, Total Receivables, Invoices, Policies, with sortable headers, an org Total row, and the mandated '(unattributed historical sales)' row showing real figures ($288,971.01 across 309 invoices / 285 policies). Named per-outlet rows are not yet populated because no sales are attributed to outlets (buyer-funnel outlet selector is out of scope per the ticket); the rollup, unattributed bucket, and total all render correctly. Screenshot eval-4-financials-by-outlet.png.
tests_ok: true · quality: Portal diff (9 files) is clean and matches conventions: useTranslation t(), localeDateTag/toLocaleDateString, reuse of existing Button/Input/Badge/Dialog/ConfirmDialog/MultiSelect/Switch/FilterDropdown, data-testids, and loading/empty/error states on every screen. Backend (api worktree, +1227 lines) adds org-scoped (RLS) Outlet/ProductOutlet models, CRUD + product-assignment + revenue-by-outlet routes, schemas, permissions, and apitests. Embed worktree has no diff (N/A, correct per scope). Minor notes (non-blocking): the policy Outlet filter sends only the first selected outletId (single-outlet match, documented in a code comment); statusBadge handles an 'inactive' status not in the ticket's active/archived set (harmless). No security or correctness issues found in the diff.