platform Case study
Brand Catalogue Sites
A watch distributor with a dealer network represents brands that each deserve a storefront of their own, but there is one product database and one team keeping it right. I built four brand catalogue sites that read that database and never write to it, and beside them three pieces of dealer-side tooling: an online-reseller approval flow, a brand menu that only claims what it can prove, and a price watch over the dealers themselves.
The business problem
The distributor already ran two sister sites on one product database: a WooCommerce shop and a custom PHP catalogue and ordering app for dealers, with role-based dashboards. Each represented brand still needed a site of its own, Persian first and with real copy, but a second copy of the catalogue would start drifting the day after launch. The brand sites had to read the central data and stay out of its way.
The dealer side had gaps of its own. Any dealer could claim to sell online and nobody checked. The shop's brands mega-menu grouped brands by country from a product attribute, and thin data had put whole brands under the wrong flag. And nobody was measuring what the dealers actually charged against the distributor's own reference prices.
What I delivered
- Four Persian-first brand catalogue sites as one plain-PHP app that reads the distributor's WooCommerce database read-only, with an hreflang English version. Zero cross-brand overlap: every product belongs to exactly one brand's site.
- Original copy throughout. Every source product had an empty description, so nothing on the brand sites duplicates the central shop. a few thousand images, all served as WebP.
- Commerce on the brand sites: a session basket, a validated Persian checkout form, and orders written to the brand account's own database. Payment sits behind a gateway interface that records the order and promises a callback until the owner names a bank.
- An online-reseller flow in the dealer app: a dealer ticks 'I have a website' at registration, their consultant approves or rejects, and approved dealers get two Excel downloads. Every grant, revoke and download is logged.
- A brands mega-menu grouped by country from a hand-curated map: 17 country groups, and 22 brands deliberately left in 'other brands' because a wrong guess is worse than no label.
- A market watch with two audiences from one collector: a rival view that compares competitor shops against the catalogue, and a manager-only dealer-inspection view that checks the resellers' own sites against the distributor's nine-brand catalogue. History lives in a compact SQLite store of state and change rows.
Technical approach
- Read-only by construction. The brand sites never write to the central database; orders land in each brand account's own database, and placing an order does not decrement central stock. Reconciliation is manual, and I say so rather than pretend the two are in sync.
- A brand label needs evidence. The attribute-derived menu had produced 58 Swiss, 56 Japanese and 1 Chinese categories, and a single tagged product out of 653 had put a whole brand under Switzerland. The evidence rule now demands half the products, or five or more tagged with 90% agreement, and the live menu takes each brand's country from a curated map instead. Two caches hide any change, a daily-rebuilt map option and the navigation-menu cache, so both are flushed together.
- Compare only where both sides hold stock. In the price watch, 55 of 70 'dearer' items were simply out of stock on our side and pushed the average to +139% against +18% for the real ones. Each side is averaged separately, brand names are normalised, and the currency unit is detected from the median ratio, because one site publishes Rial and read as +900%.
- The reseller site watch runs at 10:00 and 18:00, parses JSON-LD products and matches by SKU or URL slug; it accepts x1, /10 and x10 when comparing prices because other shops publish the multiplied number. I validated it on the distributor's own catalogue first: 10 pages crawled, 9 matched, every price and stock right.
- Consultants have no Telegram id in the app, so reseller warnings arrive as in-app notifications plus web push. Discontinuing a brand is one operation: mark its products out of stock in the shared database and remove it from the app's brand list.
Result and evidence
Two of the four brand sites are live as of August. The third is built and waits on DNS delegation; the fourth's domain was not yet registered at last check and runs under a staging subdomain. The reseller flow went live in September with the dealer accounts on file and none yet approved as an online reseller, so the Excel downloads have not reached a real reseller yet. The country-grouped menu has been live since September with 17 groups and 22 brands in 'other brands'. The market watch was built in September: on day one its store held every tracked reference in a couple of megabytes, replacing a daily JSON of about 0.5 MB per site, and a re-scan of a full dealer catalogue wrote only the handful of rows that had actually changed.
Commercial value
A distributor that represents brands needs each brand to look like itself online without the catalogue splitting into copies that drift. Reading one database read-only gives exactly that, and the dealer tooling protects the margin the brands depend on: only an approved reseller sees trade data, the menu never mislabels a brand, and a dealer selling under the reference price shows up on a card instead of in a complaint.
Readable implementation brief
implementation_brief {
project: "Brand Catalogue Sites"
status: "two sites live; third built, awaiting DNS delegation;
fourth on a staging subdomain, domain unregistered"
source: "one WooCommerce product DB, read-only; zero cross-brand
overlap; every source description was empty"
commerce: "session basket, validated Persian checkout, orders in
the brand account's own DB; central stock untouched"
resellers: "'I have a website' at registration -> consultant
approves -> two Excel downloads; every grant, revoke
and download logged"
brand_menu: "hand-curated country map, 17 groups; 22 brands left
in 'other brands' rather than guessed"
market_watch: "one collector; rival view + manager-only dealer
inspection; SQLite state + change rows"
accuracy: "compare only where both sides hold stock; currency
unit detected from the median ratio"
}What this project shows
The 'other brands' bucket is the decision I would want a reviewer to notice. Twenty-two brands sit there unlabelled because the data could not prove where they are from, and a wrong flag on a brand page is worse than none.
The market watch's accuracy rules are scar tissue: six of them, each learned from a wrong number on a card, including the day the two views shared one file and the rival run overwrote the inspection report. Measuring against your own catalogue first is cheap, and it is the only reason I trust the dealer numbers.