Ecommerce
Product catalogue at scale: variants, bulk pricing, trade pricing and per-branch overrides
Most South African catalogues break at the same point: the moment one product needs to be sold at three different prices to three different people. A retail shopper, a trade account and a branch running a promotion are three prices for one SKU, and platforms that model price as a single field cannot do it without duplicating products.
GoGee models each of those cases explicitly, in separate tables, with database-level access rules. This article documents how.
GoGee feature series · 9 of 33
How it's actually built
- Variants
- product_variants with per-variant SKU and price override
- Quantity breaks
- product_price_tiers with minimum quantity of 2 or more
- Trade pricing
- business_price_tiers, readable only by business-role users
- Branch pricing
- Price override on the branch inventory record
- Scheduling
- Tier start and end timestamps, plus an active flag
- Order integrity
- Cart and order lines reference the variant chosen
- 01Input
Product and variant
product_variants with SKU override
- 02Database
Quantity breaks
product_price_tiers from a minimum quantity of 2
- 03Database
Trade pricing
business_price_tiers, readable only by business-role users
- 04Database
Branch override
Price override on the branch inventory record
- 05Output
Price on the order line
Cart and order lines reference the chosen variant
Guardrails and fallbacks
- Tier start and end timestamps
- Active flag per tier
- Row-level access by role
Product catalogue at scale, data flow, generated from the shared GoGee feature diagram template.
Variants are rows, not text fields
A variant is its own record with a label, its own SKU, an optional price override, an optional compare-at price, its own image and a sort order, plus an active flag. Public reads only ever return active variants; only admins can write them.
Because cart lines and order lines reference the variant identifier, an order records exactly which 5-litre-blue-with-lid version was bought. That sounds obvious until you have to resolve a delivery dispute on a platform where variants were stored as a comma-separated string.
Buy-more-save-more is a first-class table
Quantity-break pricing lives in its own table with a minimum quantity, the bundle price, the normal price for comparison, and optional start and end timestamps. Only active tiers are publicly readable, and tiers are attached to products at the point results are returned, including search results, so a shopper sees the bulk price on the listing rather than discovering it at checkout.
Because tiers carry their own dates, a seasonal bulk promotion can be loaded weeks ahead and expires without anyone remembering to switch it off.
Trade pricing is locked at the database, not the interface
Business pricing is a separate table keyed by SKU with its own quantity breaks, tied to business accounts linked to real user records. Read access is granted only to users holding the business role, enforced by row-level security in Postgres.
The distinction matters: hiding trade prices in the front end is a design decision, while enforcing it in the database is a security control. There is also a pending state, so an applicant for a trade account cannot see trade pricing before you approve them.
- Trade prices are invisible to retail shoppers at the data layer
- Business accounts map to real authenticated users
- A pending role exists for accounts awaiting approval
- Quantity breaks apply on top of trade pricing
Per-branch pricing rides on the stock record
Rather than a fourth pricing table, a branch overrides price on its own inventory row for that product. It is the right shape: the branch that holds the stock is the branch that can price it, and if that branch does not stock the product there is nothing to override.
The same record carries quantity on hand, quantity reserved, an in-store-only flag and a low-stock threshold, so pricing and availability stay in one place per branch.
Getting thousands of products in and keeping them fresh
Catalogues at this size are not typed in. Products, promotions, users and suppliers come in by mapped CSV upload, supplier XML feeds are ingested on a schedule with de-duplication, and a supplier portal lets suppliers submit price lists and new products for approval instead of emailing spreadsheets.
Every product also carries its own price history, which is how margin drift and supplier creep get noticed before the annual review rather than after it.
Questions we get asked
How many products can it handle?
Catalogues in the thousands of SKUs with variants, tiers and per-branch stock are the design target, with indexed search built for that scale.
Can one product have retail, bulk, trade and branch pricing at once?
Yes. They are separate mechanisms, quantity tiers, role-gated business tiers and a branch inventory override, resolved for the specific shopper and branch.
Do trade customers log in to see their prices?
Yes. Trade pricing is only readable by users holding the business role, so it requires authentication by design.
Read next
Multi-branch
Stock availability across branches
GoGee holds stock per branch with reserved quantities and low-stock thresholds, ranks the nearest branch by di…
Franchise
Franchise & multi-branch hub
GoGee's franchise model uses branch-scoped roles and row-level security so head office works globally while fr…
AI content
AI Product Names & Updates
How GoGee rewrites product names and descriptions in bulk: Gemini generates proposals into a review queue, vis…

