Files
vmall/openspec/changes/replace-mock-api-wave-6/specs/brand/spec.md
T
james 65cea42da6 docs(openspec): plan wave 6, brands and real sales
Planning only: proposal, delta specs, design and tasks. No code yet.

This is the last substantive in-scope box in docs/TBD-migrate-wave.md. Wave 1
removed the brand facet and the sales/comments sorts for want of a model; sales
are derivable from order_items and a brand model is a table plus a column, so
both come back for real.

Decisions recorded in the design:

- brands are reference data in their own table with a nullable products.brand_id
  and an ordered replace endpoint, mirroring categories and storefront content
- a "sale" is a unit on an order that reached payment; pending and cancelled
  orders do not count, so an abandoned checkout cannot inflate the figure
- sold_count is computed per read rather than stored, so it cannot drift from
  the orders that produced it
- the review UI is removed rather than relabelled: the mall attributes invented
  comments to named shoppers and shows a "good rate", which a migration that
  makes everything else real has no business keeping on screen. Reviews are
  recorded as a separate future capability

openspec validate --strict passes and the change is ready to apply.
2026-09-17 17:17:52 +00:00

1.7 KiB

Spec Delta

Purpose

The product brand registry: a bilingual, admin-managed list of manufacturers that products reference, shoppers filter listings by, and the storefront shows on a product.

ADDED Requirements

Requirement: Public brand list

GET /api/brands SHALL return the brands without authentication, ordered by their stored position, each with an id, slug and bilingual name. Products SHALL carry a nullable brand_id referring to that list, and GET /api/products SHALL accept a brand_id filter that composes with the existing category, shop and keyword filters.

Scenario: filter a listing by brand

  • WHEN a shopper filters the catalog by a brand
  • THEN only that brand's published products are returned, and the filter combines with a category filter rather than replacing it

Scenario: a product without a brand

  • WHEN a product has no brand assigned
  • THEN it is still listed and its brand_id is null rather than pointing at an invented brand

Requirement: Brand management

A platform admin SHALL replace the brand list with PUT /api/admin/brands, which validates each entry, applies as one transaction and reindexes positions from the submitted order; the write SHALL require the platform_admin role. Each entry SHALL carry a slug and non-empty en and zh names, and a rejected list SHALL leave the stored brands untouched.

Scenario: replace round-trips

  • WHEN an admin replaces the brands and then reads them publicly
  • THEN the public list matches, in the submitted order

Scenario: incomplete bilingual name is refused

  • WHEN an admin submits a brand with only en text
  • THEN the request fails and the stored brands are unchanged