HOCO B2B
SRSRequirements

Catalog

Brands, the category tree with sub-categories, products and variants with filterable attributes, lifecycle, visibility, import/export, search with advanced filters, product detail, favorites, barcode scan and home merchandising.

This area covers the B2B catalog of HOCO, ACEFAST and BOROFONE accessories: how the back-office (BO) maintains it and how the reseller app (RA), the reseller web (RW) and the delegate's assisted order (DA) browse it (cahier §4, §4.3, §5, §5.1, §5.2, §13.2, §15.4, reference screens 1–4, and the decisions of 17 Oct 2026 on guest mode, advanced filters and attributes). The server's catalog module owns Brand, Category, Product, ProductVariant, ProductImage, AttributeDefinition, VariantAttributeValue, ProductVisibilityRule and Favorite; prices come from the pricing module (PRC-02) and availability from inventory (STK-07). Operations: API › Catalog & favorites for the apps, API › Catalog & pricing for the back-office; statuses on Lifecycles › Product.

Two rules hold on every catalog read: a guest sees no price (AUTH-10) and nobody outside the back-office sees a stock quantity — only En stock or En rupture.

IDRequirementReleasePrioritySourceApps
CAT-01Brands HOCO, ACEFAST and BOROFONE managed as dataV1MUST§4, §4.3API · BO
CAT-02Category tree (chargers, cables, earphones, power banks, car holders, audio, watches, accessories)V1MUST§4.3API · BO
CAT-03Product data: brand, reference, name, category, description, characteristics, images, barcode, warranty, variantsV1MUST§4.3, §15.4API · BO
CAT-04Product lifecycle: draft, active, hidden, out of stock (automatic), archivedV1MUST§4.3API · BO
CAT-05Visibility and activation per reseller or segmentV1MUST§4.3API · BO
CAT-06Product import/export (Excel/CSV) with a validation reportV1MUST§4.3, §13.2API · BO
CAT-07Catalog browsing and search by name, reference or brand, with sorting; results in under 3 secondsV1MUST§5.1RA · RW · DA · API
CAT-08Product detail: gallery, specifications, description, prices, availability, quantity limits, similar/complementary products, shareV1MUST§5.2RA · RW · API
CAT-09FavoritesV1SHOULD§5.1, §5.2RA · RW · API
CAT-10Barcode scan to find a productV1COULDReference screen 1RA
CAT-11Home merchandising: popular categories and recommended products (rules-based)V1SHOULD§5RA · RW · API
CAT-12Advanced, combinable filters on web and mobile: price range, category and sub-category, brand, attributes (power, connector, cable length, colour…), availability, promotion, new arrivals — with result countsV1MUSTTeam decision (17 Oct 2026)RA · RW · API
CAT-13Filterable product attributes defined per category (type, unit, allowed values) and filled on every variantV1MUSTTeam decision (17 Oct 2026)BO · API

Settings used in this area

SettingDefaultMeaning
catalog.newProductDays30A product published within this many days is a Nouveauté (badge, tab, filter new).
catalog.pricesVisibleBeforeValidationtruePending accounts see the prices of their declared type (AUTH-08).
pricing.defaultVatRate1900VAT rate proposed for new variants (basis points).

Requirements

CAT-01 — Brands managed as data

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4, §4.3API · BOSRV-11 · DSH-04

Statement — The system shall manage the brands HOCO, ACEFAST and BOROFONE — and any brand added later — as data, not as code.

Rules

  1. A Brand has a unique name (2–60 characters), a slug, a public logo (BRAND_LOGO), a description, a sort order and a status (ACTIVE, ARCHIVED). The three brands are seeded by the first migration (SRV-02).
  2. Brands are created, edited and archived in BO-29 (catalog.create, catalog.update, catalog.delete); a name or slug already used answers 409 DUPLICATE_VALUE.
  3. A brand referenced by products is archived instead of deleted; an archived brand disappears from filters and from the brand picker of new products, and its products keep it.
  4. catalog.brands returns active brands that have at least one product visible to the caller (guests included), in sort order, with their logo URL from the CDN.
  5. No behaviour depends on a brand's name: brand-specific rules (warranty, promotions) are data on products or promotions.

Acceptance criteria

  • After the seed, catalog.brands returns HOCO, ACEFAST and BOROFONE with their logos.
  • Creating a second brand named hoco returns 409 DUPLICATE_VALUE.
  • Archiving a brand with products keeps those products' brand and removes the brand from the reseller filter.

Data — Brand, File.

Interfaces

CAT-02 — Category tree with sub-categories

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.3API · BOSRV-11 · DSH-04

Statement — The system shall organise products in a two-level tree of categories and sub-categories, starting with chargers, cables, earphones, power banks, car holders, audio, watches and accessories.

Rules

  1. A Category has a name unique among its siblings, a slug, an optional icon, a sort order, a status and a parent: top-level categories have none, sub-categories have a top-level parent. Depth is at most 2 (422 CATEGORY_DEPTH_EXCEEDED). The eight categories of cahier §4.3 are seeded: Chargeurs, Câbles, Écouteurs, Power banks, Supports voiture, Audio, Montres, Accessoires.
  2. A product belongs to exactly one category or sub-category; a category matches its sub-categories in every filter and count.
  3. BO-30 edits the tree; reordering and re-parenting (admin.categories.reorder) refuse cycles (422 CATEGORY_CYCLE) and depth breaches. Deleting a category that has sub-categories or products answers 409 RESOURCE_IN_USE; archiving one with active products or active children answers the same.
  4. Attribute definitions are attached to categories and inherited by their sub-categories (CAT-13).
  5. catalog.categories returns the tree with, for each node, the number of products visible to the caller; nodes with no visible product are omitted.

Acceptance criteria

  • Creating USB-C under Câbles succeeds; creating a node under USB-C returns 422 CATEGORY_DEPTH_EXCEEDED.
  • Filtering the catalog on Câbles returns the products of Câbles and of its sub-categories.
  • Deleting Câbles while it holds products returns 409 RESOURCE_IN_USE.
  • A sub-category whose products are all hidden does not appear in the reseller's tree.

Data — Category, AttributeDefinition (category links), Product.

Interfaces

CAT-03 — Product and variant data

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.3, §15.4API · BOSRV-11 · DSH-04 · DES-17

Statement — The system shall hold, for every product, the data of cahier §15.4 — brand, reference, name, category, description, characteristics, images, barcode, warranty — and its variants with their identifiers, attributes, prices and quantity limits.

Rules

  1. Product: brand, reference (unique per brand, e.g. HC-X14), name, category, description (plain text, at most 5 000 characters), free characteristics (label/value pairs, e.g. Puissance / 30 W), up to 12 ordered images (PRODUCT_IMAGE, public CDN, JPEG/PNG/WebP at most 5 MB; an image may belong to one variant), warranty (months and text), status (CAT-04), visibility (CAT-05).
  2. Variant (at least one per product): SKU (unique, immutable once ordered — 409 RESOURCE_IN_USE), barcode (EAN-13/UPC, unique), name (Noir · 1 m), attribute values (CAT-13), the three selling prices, VAT rate, recommended and purchase prices (PRC-01), quantity limits minQty (≥ 1), qtyStep (≥ 1) and optional maxQtyPerOrder (≥ minQty), weight, status. Two variants of one product cannot carry exactly the same attribute values (409 DUPLICATE_VALUE).
  3. A new variant gets stock rows at 0 in every active warehouse; the last active variant of an ACTIVE product cannot be hidden, archived or deleted (409 VARIANT_LAST_ACTIVE).
  4. Writing any price field needs pricing.update in addition to catalog.update (403 PERMISSION_DENIED with the refused fields); the three prices must be ordered priceSuperGros ≤ priceDemiGros ≤ priceDetail (422 PRICE_ORDER_INVALID).
  5. The editor (BO-28) shows a completeness indicator listing what blocks publication (CAT-04 R2).
  6. Every change writes AuditLog; price changes also write PriceChange rows (AUD-02).

Acceptance criteria

  • Creating product HC-X14 twice for HOCO returns 409 DUPLICATE_VALUE; the same reference under ACEFAST is accepted.
  • A variant with barcode 6931474761234 already used returns 409 DUPLICATE_VALUE.
  • Prices 35 000 / 32 000 / 39 000 (super-gros above demi-gros) return 422 PRICE_ORDER_INVALID.
  • A stock manager without pricing.update saving a price gets 403 PERMISSION_DENIED naming priceDetail; saving the description succeeds.
  • Changing the SKU of a variant that has been ordered returns 409 RESOURCE_IN_USE.

Data — Product, ProductVariant, ProductImage, VariantAttributeValue, StockLevel, PriceChange, File, AuditLog.

Interfaces

CAT-04 — Product lifecycle

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.3API · BOSRV-11 · DSH-04

Statement — The system shall manage each product through the statuses draft, active, hidden, out of stock (automatic) and archived, and show resellers only active and out-of-stock products.

Rules

  1. Transitions (Lifecycles › Product): DRAFT or HIDDEN → ACTIVE (publish); ACTIVE or OUT_OF_STOCK → HIDDEN; any → ARCHIVED; ARCHIVED → HIDDEN (restore, catalog.administer). ACTIVE ↔ OUT_OF_STOCK is automatic only: OUT_OF_STOCK when every active variant's available stock is 0, back to ACTIVE on restock (receipt, return reception, cancellation). Any other move answers 409 PRODUCT_TRANSITION_NOT_ALLOWED.
  2. Publishing requires the minimum data of §15.4: brand, reference, name, category, description, at least one image, VAT rate, at least one active variant with SKU and its three prices, and a value for every attribute marked requiredToPublish — otherwise 422 PRODUCT_INCOMPLETE with details.missing. The first publication sets publishedAt (drives Nouveautés).
  3. Resellers, guests and delegates see ACTIVE and OUT_OF_STOCK products only; OUT_OF_STOCK products show « En rupture » and cannot be added to a cart. DRAFT, HIDDEN and ARCHIVED never appear and their detail answers 404 NOT_FOUND.
  4. Hiding or archiving takes effect at the next read: the product leaves lists, search, favorites and promotion pages; cart lines on it become blocking issues PRODUCT_UNAVAILABLE (ORD-02); past orders keep showing it.
  5. Every status change is audited with its reason; the automatic changes have the system as actor.
ValueUI label (fr)Meaning
DRAFTBrouillonBeing prepared; invisible to resellers.
ACTIVEActifVisible and orderable (subject to visibility rules).
HIDDENMasquéKept but invisible to resellers.
OUT_OF_STOCKEn ruptureSet automatically when every variant's available stock is 0; back to ACTIVE on restock.
ARCHIVEDArchivéRetired; remains referenced by past orders.

Acceptance criteria

  • Publishing a product without image returns 422 PRODUCT_INCOMPLETE with missing: ["images"].
  • to: OUT_OF_STOCK by a user returns 409 PRODUCT_TRANSITION_NOT_ALLOWED.
  • When the last available unit of every variant is reserved, the product shows En rupture in the catalog within 1 minute; a goods receipt brings it back to En stock.
  • A hidden product's detail returns 404 NOT_FOUND and a cart holding it shows PRODUCT_UNAVAILABLE.

Data — Product (status, publishedAt), ProductVariant, StockLevel, AuditLog.

Interfaces

CAT-05 — Visibility per segment or reseller

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.3API · BOSRV-11 · DSH-04

Statement — The system shall let the back-office restrict who sees and orders a product — everyone, only some segments or resellers, or everyone except some — and apply that rule on every reseller-facing read and order path.

Rules

  1. A product's audience mode is EVERYONE (default), INCLUDE_ONLY (only the listed segments and resellers) or EXCLUDE (everyone except them), stored as ProductVisibilityRule rows (admin.products.visibility, catalog.update). INCLUDE_ONLY or EXCLUDE with empty lists, or EVERYONE with lists, answers 400 VALIDATION_FAILED.
  2. A reseller matches a rule through its segments (CRM-06) or its id; a reseller both included and excluded is excluded.
  3. Applied everywhere: catalog lists, counts and facets, search suggestions, product detail (404 NOT_FOUND when hidden for the caller), home blocks, favorites, promotions, cart, checkout, back-office orders on behalf (ORD-11) and assisted orders — where the rule is evaluated for the reseller, not for the delegate.
  4. Guests see only EVERYONE products (AUTH-10).
  5. A visibility change applies at the next read; a cart line that is no longer visible becomes PRODUCT_UNAVAILABLE. Audit UPDATE with the audience before and after.

Acceptance criteria

  • A product in INCLUDE_ONLY for segment Grossistes Oran appears for a reseller of that segment and not for another reseller, nor for a guest.
  • A delegate preparing an assisted order for a reseller outside the audience cannot find the product.
  • Excluding a reseller who holds the product in their cart turns the line into PRODUCT_UNAVAILABLE at the next cart.get.

Data — ProductVisibilityRule, Segment, ResellerSegment, Product.

Interfaces

CAT-06 — Product import and export with a validation report

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §4.3, §13.2API · BODSH-04 · SRV-27 · DES-17

Statement — The system shall import products from an Excel or CSV file after a dry run that reports every row's result, and export the catalog to Excel or CSV.

Rules

  1. Import (BO-32, admin.products.import, catalog.administer): upload the file (IMPORT_FILE), run DRY_RUN, read the row-by-row report (VALID, WARNING, ERROR with codes), then APPLY the dry run within 24 hours (409 IMPORT_DRY_RUN_EXPIRED after; 409 IMPORT_ALREADY_APPLIED twice; 422 IMPORT_DRY_RUN_REQUIRED if it failed).
  2. Columns (one row per variant, grouped by reference): brand*, category path* (Câbles > Câbles USB-C), reference*, name*, description, specs (label=value; …), warranty months, sku*, barcode, variant name, one attr:<key> column per attribute of the category, the three HT prices* (price_super_gros_ht, price_demi_gros_ht, price_detail_ht, in DA with comma decimals), recommended price TTC, purchase price HT, VAT %, min qty, step, max per order, weight, status (DRAFT, ACTIVE, HIDDEN), image URLs. Price columns need pricing.update.
  3. Validation is exactly that of the single operations (references, SKUs, barcodes, attribute values, price order, quantity rules); at most 5 000 rows per file (422 IMPORT_FILE_INVALID with details.maxRows or missingColumns). APPLY writes valid and warning rows in batches of 200, upserting on SKU; rows that became invalid are SKIPPED.
  4. Imported prices write PriceChange rows with source IMPORT; stock is not imported here (ADM-06).
  5. Export (admin.products.export, catalog.export): an asynchronous XLSX or CSV of the filtered list in the import format, so an export can be edited and re-imported; the purchase price column only for holders of pricing.view. The requester is notified when the file is ready.
  6. Both are journaled (IMPORT, EXPORT).

Acceptance criteria

  • A 300-row file with 4 duplicate SKUs gives a dry run with 296 VALID rows and 4 ERROR rows naming the SKU; applying writes 296 variants.
  • A file of 6 000 rows returns 422 IMPORT_FILE_INVALID with maxRows: 5000.
  • Applying a 25-hour-old dry run returns 409 IMPORT_DRY_RUN_EXPIRED.
  • An export re-imported without changes produces no change and no PriceChange row.
  • A stock manager's export has no purchase price column.

Data — ImportJob, ExportJob, File, Product, ProductVariant, VariantAttributeValue, PriceChange, AuditLog.

Interfaces

CAT-07 — Catalog browsing and search

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §5.1RA · RW · DA · APISRV-11 · WEB-02 · MRA-02 · DES-07

Statement — The system shall let resellers, guests and delegates search the catalog by name, reference or brand, filter it and sort it, returning results in under 3 seconds on a normal mobile connection.

Rules

  1. Search q (2–80 characters) on product name, model reference, variant SKU, barcode (exact), brand and category names, case- and accent-insensitive with typo tolerance (unaccent + trigram). Type-ahead suggestions (catalog.products.suggest) return products, brands and categories after 2 characters, debounced 250 ms in the apps, without prices.
  2. Filters are those of CAT-12; sorting: relevance (default with q: exact SKU, barcode or reference first, then name prefix, then similarity, then popularity), popular (default otherwise: units ordered over 30 days, refreshed daily), newest, and — for signed-in callers only — price_asc / price_desc on the caller's effective TTC unit price.
  3. The tabs of reference screen 3 map to Nouveautés (new, newest), Populaires (popular) and Promotions (promotion); category chips set categoryId.
  4. Cursor pagination (20 per page, at most 100); infinite scroll in the app, pages on the web. Out-of-stock products are listed after in-stock ones under relevance and popular.
  5. Each card carries name, brand, image, the caller's price (null for guests), promotion price and label, availability (IN_STOCK/OUT_OF_STOCK), Nouveauté badge and favourite flag.
  6. A delegate passes the resellerId of a reseller in scope to see that reseller's prices and visibility (403 OUT_OF_SCOPE otherwise).
  7. Performance: server p95 under 800 ms for a page of 20 with filters on 5 000 products; end to end under 3 s on the reference device and network of NFR-01; thumbnails served from the CDN in WebP.
  8. The first catalog read by a reseller account records the invitation step CATALOG_VIEWED (IVT-03).

Acceptance criteria

  • Searching ecouteur finds products named Écouteurs; HC-X14 returns that product first; a barcode returns its product.
  • A guest sending sort=price_asc gets 400 VALIDATION_FAILED (SIGN_IN_REQUIRED).
  • On the reference phone over the reference network, opening RA-12 and applying a filter each show results in under 3 seconds (measured in MOB-12).
  • A delegate listing with a resellerId outside their scope gets 403 OUT_OF_SCOPE.
  • No card in any response carries a stock quantity.

Data — Product, ProductVariant, Brand, Category; a denormalised search view refreshed on product, price, availability and visibility changes.

Interfaces

CAT-08 — Product detail

ReleasePrioritySourceAppsOwner tasks
V1MUSTcahier §5.2RA · RW · APISRV-11 · WEB-02 · MRA-03 · DES-08

Statement — The system shall show a product page with its gallery, characteristics, description, the caller's price and the recommended retail price, availability, quantity limits, variants, similar and complementary products, and a share action.

Rules

  1. catalog.products.get returns the gallery, name, brand, reference, category path, description, characteristics, warranty, variants (name, attributes, the caller's price, availability, limits minQty / qtyStep / maxQtyPerOrder, quantity already in the cart) and the active promotion with its end time.
  2. Price: the caller's customer-type price with HT, TTC and VAT, and the recommended retail price (« Prix conseillé ») — both null for guests, with the lock of RA-41. Availability shows En stock or En rupture only: the « Plus que N pièces » hint of the reference mock-up is not implemented (STK-07).
  3. The quantity stepper starts at minQty, moves by qtyStep and stops at maxQtyPerOrder (« Max : 20 pièces par commande »), which applies to the cart total of the variant. Stock is not a limit the app knows: an unavailable quantity is refused by the cart (ORD-02).
  4. « Ajouter au panier » adds the selected variant and quantity; « Commander maintenant » adds it and opens the cart (no single-product checkout). For guests both open the sign-up prompt.
  5. Similar products: same sub-category, price within ±40 % of this one, ranked by popularity, completed from the parent category when fewer than 4. Complementary products: categories linked to this one in the catalog settings (chargers → cables and power banks; earphones → audio accessories; car holders → car chargers). Both lists show in-stock, visible products only.
  6. Share: a link to the product page (app.hoco.dz, opening the app when installed) with name, reference and image — never a price, since prices depend on the customer type.
  7. The product added to the cart keeps the right reference and quantity, and the price the cart computes is authoritative (cahier §5.2 acceptance — PRC-05).

Acceptance criteria

  • The page of HC-N32 for a Demi-gros reseller shows the Demi-gros TTC price, the recommended price and En stock; for a guest both prices are replaced by the lock.
  • With minQty 5, qtyStep 5, maxQtyPerOrder 100, the stepper offers 5, 10, … 100 and nothing else.
  • The shared text contains the product name and reference and no amount.
  • A hidden product's link opens « Produit indisponible » (404 NOT_FOUND).

Data — Product, ProductVariant, ProductImage, VariantAttributeValue, Promotion, CartItem (quantity in cart).

Interfaces

CAT-09 — Favorites

ReleasePrioritySourceAppsOwner tasks
V1SHOULDcahier §5.1, §5.2RA · RW · APISRV-11 · WEB-02 · MRA-03

Statement — The system should let a reseller keep a list of favourite products shared between the app and the web.

Rules

  1. Favorites belong to the reseller (shared by the app and the web) and need a session; a guest tapping the heart is prompted to sign up (AUTH-10).
  2. favorites.add and favorites.remove are idempotent (adding twice keeps one); at most 500 favorites.
  3. favorites.list returns visible products with the caller's price and availability; products that became hidden, archived or invisible to the reseller are left out, out-of-stock ones stay with En rupture.
  4. Delegates have no favorites (403 PERMISSION_DENIED).

Acceptance criteria

  • A product added on the phone appears in the web favorites.
  • Adding the same product twice leaves one favorite; removing it twice answers success both times.
  • A favourite product that is archived disappears from the list.

Data — Favorite, Product.

Interfaces

CAT-10 — Barcode scan

ReleasePrioritySourceAppsOwner tasks
V1COULDreference screen 1RASRV-11 · MRA-02 · DES-07

Statement — The system could let a reseller find a product by scanning its barcode with the phone camera.

Rules

  1. The scanner (RA-11, mobile_scanner) reads EAN-13, EAN-8, UPC-A and Code 128 and calls catalog.products.byBarcode with 8–14 digits (400 VALIDATION_FAILED otherwise).
  2. A match opens the product page on the scanned variant, with the caller's price (none for guests) and limits.
  3. No variant with that barcode, or a product not visible to the caller, gives the same 404 NOT_FOUND; the app shows « Produit introuvable » and offers the text search with the code.
  4. The scanner needs the camera permission; refusing it shows how to enable it and keeps text search available. The delegate app uses the same operation with a resellerId.

Acceptance criteria

  • Scanning 6931474761234 opens HOCO X14 Câble USB-C 1 m, variant Noir · 1 m.
  • An unknown barcode shows « Produit introuvable » with the search pre-filled.

Data — ProductVariant (barcode).

Interfaces

CAT-11 — Home merchandising

ReleasePrioritySourceAppsOwner tasks
V1SHOULDcahier §5RA · RW · APISRV-11 · WEB-02 · MRA-01 · DES-07

Statement — The system should compose the reseller home with banners, the daily offer, quick-access tiles, popular categories, recommended products and account tiles, using rules rather than manual curation.

Rules

  1. catalog.home aggregates: banners and the daily offer (PRM-05), quick tiles (Catalogue, Commandes, Promotions, Points, Assistance; Points hidden until loyalty is enabled in V2), popular categories, recommended products and account tiles (balance, available credit — FIN-10).
  2. Popular categories: the 8 categories with the most units ordered over the last 30 days among products visible to the caller.
  3. Recommended products (up to 12, in stock and visible): first the products the reseller ordered most over 90 days, then best sellers of the reseller's wilaya, then new arrivals in the categories they buy; for guests and resellers without orders, national best sellers and new arrivals.
  4. Each block is computed independently within 300 ms; a block that fails returns empty and is logged, so the home always opens (cahier §5 acceptance).
  5. Guests get the same home without prices, with the account tiles replaced by the sign-up invitation and Commandes / Assistance tiles disabled.

Acceptance criteria

  • A reseller who often orders X14 cables sees them first in Produits recommandés.
  • With the analytics block failing, the home still renders with the other blocks.
  • A guest's home shows banners and products with locks, and no balance tile.

Data — Product, Category, Order and OrderItem aggregates, Banner, Promotion, Reseller.

Interfaces

CAT-12 — Advanced combinable filters with counts

ReleasePrioritySourceAppsOwner tasks
V1MUSTTeam decision (17 Oct 2026)RA · RW · APISRV-11 · WEB-02 · MRA-02

Statement — The system shall offer, on the web and on mobile, combinable filters — price range, category and sub-category, brand, product attributes, availability, promotion and new arrivals — with the number of results for each choice.

Rules

  1. Filters (shared by catalog.products.list and catalog.facets): categoryId (a category or a sub-category), brandId (several), priceMin / priceMax (TTC centimes on the unit price the caller would pay, promotion included — signed-in only), attr.<key> (several values = any of; numeric ranges attr.power_w=20..65, open ends 10000..), availability, promotion, new.
  2. Values of one filter are OR-ed, different filters AND-ed. Attribute filters apply to variants: a product matches when one of its visible variants matches every attribute filter, and its card then shows that variant.
  3. Facets with counts (catalog.facets): categories and sub-categories, brands, price range (not for guests), the attributes of the selected category and its parent (at most 12, in the order set in BO-71), availability, promotion, new arrivals. Counts are disjunctive — each group is counted with every filter except its own — so the user sees what another choice would give; the overall total equals the list's.
  4. Unknown attribute keys return 400 VALIDATION_FAILED (UNKNOWN_ATTRIBUTE), inverted ranges INVALID_RANGE, price filters from a guest SIGN_IN_REQUIRED.
  5. UI: a bottom sheet on mobile (RA-13) and a sidebar on the web (RW-03); active filters are shown as removable chips with « Tout effacer »; filters persist while browsing and are reset by a new search.
  6. Facets answer within the same performance budget as the list (CAT-07).

Acceptance criteria

  • Selecting Chargeurs, brand HOCO and attr.power_w=20..65 returns only chargers of 20 to 65 W; the brand facet still shows ACEFAST with its count.
  • attr.connector=USB_C&attr.connector=LIGHTNING returns products having either connector.
  • The facets total equals the number of products the list returns for the same filters.
  • A guest's facets have no price range; a guest price filter returns SIGN_IN_REQUIRED.

Data — AttributeDefinition, VariantAttributeValue, Product, ProductVariant, Category, Brand.

Interfaces

  • Screens: RA-12 Catalog · RA-13 Advanced filters & sort (price range, category/sub-category, brand, attributes…) · RW-03 Catalog with advanced sidebar filters & search · BO-71 Product attributes (filters) per category
  • API: catalog.facets GET /catalog/facets

CAT-13 — Filterable attributes per category

ReleasePrioritySourceAppsOwner tasks
V1MUSTTeam decision (17 Oct 2026)BO · APISRV-11 · DSH-04 · MRA-02

Statement — The system shall let the back-office define filterable attributes per category — key, label, type, unit and allowed values — and require their values on the variants of that category.

Rules

  1. An AttributeDefinition has an immutable snake_case key (power_w, connector), labels in fr/ar/en, an immutable type (NUMBER with unit and 0–2 decimals, ENUM with coded values, BOOLEAN, TEXT), a multi-value flag (a 3-in-1 cable has three connectors), the categories it applies to (inherited by sub-categories), a filter widget (CHECKBOX, RANGE, TOGGLE, NONE), a requiredToPublish flag, a sort order and a status. TEXT attributes cannot be filterable; incompatible widgets answer 400 VALIDATION_FAILED.
  2. Examples seeded with the catalog: power (W), connector (USB-C, Lightning, Micro-USB, USB-A), cable length (m), colour, battery capacity (mAh), Bluetooth version, fast-charge protocols.
  3. Each variant stores a VariantAttributeValue per applicable attribute; a value of the wrong type or an unknown ENUM code answers 400 VALIDATION_FAILED; publishing a product lacking a required value answers 422 PRODUCT_INCOMPLETE (CAT-04).
  4. BO-71 manages definitions (admin.attributes.crud, catalog.*). Removing an ENUM code in use, or deleting a definition with values, answers 409 RESOURCE_IN_USE (archive instead — the filter disappears from the apps, the values are kept). A new requiredToPublish applies to future publications; active products lacking the value are listed as incomplete but stay published.
  5. Every change invalidates the facet caches of the affected categories; values are imported with attr:<key> columns (CAT-06).

Acceptance criteria

  • Defining power_w (NUMBER, W, RANGE) on Chargeurs makes a power slider appear in RA-13 and RW-03 for chargers and their sub-categories.
  • Saving a charger variant with power_w = "fast" returns 400 VALIDATION_FAILED.
  • Deleting connector while variants use it returns 409 RESOURCE_IN_USE; archiving it removes the filter and keeps the values.
  • Marking power_w required makes publishing a charger without power return 422 PRODUCT_INCOMPLETE.

Data — AttributeDefinition, VariantAttributeValue, Category, ProductVariant.

Interfaces

  • Screens: RA-13 Advanced filters & sort (price range, category/sub-category, brand, attributes…) · BO-28 Product editor · RW-03 Catalog with advanced sidebar filters & search · BO-71 Product attributes (filters) per category
  • API: catalog.facets GET /catalog/facets · admin.attributes.crud GET · POST · PATCH · DELETE /admin/attribute-definitions[/{attributeId}]

Error codes

CodeHTTPRaised when
DUPLICATE_VALUE409Brand or category name, product reference (per brand), SKU, barcode, attribute key or variant attribute combination already used
RESOURCE_IN_USE409Deleting a used category or attribute, changing an ordered SKU, deleting a variant with stock
VARIANT_LAST_ACTIVE409Hiding or deleting the last active variant of an active product
PRODUCT_TRANSITION_NOT_ALLOWED409A product status move outside the lifecycle (e.g. setting OUT_OF_STOCK by hand)
IMPORT_DRY_RUN_EXPIRED · IMPORT_ALREADY_APPLIED409Applying an old or already applied dry run
CATEGORY_DEPTH_EXCEEDED · CATEGORY_CYCLE422Tree deeper than two levels or a cycle
PRODUCT_INCOMPLETE422Publishing without the minimum data or required attributes
PRICE_ORDER_INVALID422Prices not ordered super-gros ≤ demi-gros ≤ détail
IMPORT_FILE_INVALID · IMPORT_DRY_RUN_REQUIRED422Unreadable file, missing columns, over 5 000 rows; apply without a successful dry run
OUT_OF_SCOPE403A delegate's resellerId outside their scope
Field codes in 400 VALIDATION_FAILED400SIGN_IN_REQUIRED, UNKNOWN_ATTRIBUTE, INVALID_RANGE, INVALID_BARCODE, TOO_SHORT

On this page