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.
| ID | Requirement | Release | Priority | Source | Apps |
|---|---|---|---|---|---|
CAT-01 | Brands HOCO, ACEFAST and BOROFONE managed as data | V1 | MUST | §4, §4.3 | API · BO |
CAT-02 | Category tree (chargers, cables, earphones, power banks, car holders, audio, watches, accessories) | V1 | MUST | §4.3 | API · BO |
CAT-03 | Product data: brand, reference, name, category, description, characteristics, images, barcode, warranty, variants | V1 | MUST | §4.3, §15.4 | API · BO |
CAT-04 | Product lifecycle: draft, active, hidden, out of stock (automatic), archived | V1 | MUST | §4.3 | API · BO |
CAT-05 | Visibility and activation per reseller or segment | V1 | MUST | §4.3 | API · BO |
CAT-06 | Product import/export (Excel/CSV) with a validation report | V1 | MUST | §4.3, §13.2 | API · BO |
CAT-07 | Catalog browsing and search by name, reference or brand, with sorting; results in under 3 seconds | V1 | MUST | §5.1 | RA · RW · DA · API |
CAT-08 | Product detail: gallery, specifications, description, prices, availability, quantity limits, similar/complementary products, share | V1 | MUST | §5.2 | RA · RW · API |
CAT-09 | Favorites | V1 | SHOULD | §5.1, §5.2 | RA · RW · API |
CAT-10 | Barcode scan to find a product | V1 | COULD | Reference screen 1 | RA |
CAT-11 | Home merchandising: popular categories and recommended products (rules-based) | V1 | SHOULD | §5 | RA · RW · API |
CAT-12 | Advanced, 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 counts | V1 | MUST | Team decision (17 Oct 2026) | RA · RW · API |
CAT-13 | Filterable product attributes defined per category (type, unit, allowed values) and filled on every variant | V1 | MUST | Team decision (17 Oct 2026) | BO · API |
Settings used in this area
| Setting | Default | Meaning |
|---|---|---|
catalog.newProductDays | 30 | A product published within this many days is a Nouveauté (badge, tab, filter new). |
catalog.pricesVisibleBeforeValidation | true | Pending accounts see the prices of their declared type (AUTH-08). |
pricing.defaultVatRate | 1900 | VAT rate proposed for new variants (basis points). |
Requirements
CAT-01 — Brands managed as data
Statement — The system shall manage the brands HOCO, ACEFAST and BOROFONE — and any brand added later — as data, not as code.
Rules
- A
Brandhas 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). - Brands are created, edited and archived in BO-29 (
catalog.create,catalog.update,catalog.delete); a name or slug already used answers409 DUPLICATE_VALUE. - 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.
catalog.brandsreturns active brands that have at least one product visible to the caller (guests included), in sort order, with their logo URL from the CDN.- 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.brandsreturns 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
- Screens: BO-29 Brands
- API:
catalog.brandsGET /catalog/brands·admin.brands.crudGET · POST · PATCH · DELETE /admin/brands[/{brandId}]
CAT-02 — Category tree with sub-categories
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
- A
Categoryhas 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. - A product belongs to exactly one category or sub-category; a category matches its sub-categories in every filter and count.
- 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 answers409 RESOURCE_IN_USE; archiving one with active products or active children answers the same. - Attribute definitions are attached to categories and inherited by their sub-categories (CAT-13).
catalog.categoriesreturns 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
- Screens: BO-30 Categories
- API:
catalog.categoriesGET /catalog/categories·admin.categories.crudGET · POST · PATCH · DELETE /admin/categories[/{categoryId}]·admin.categories.reorderPUT /admin/categories/order
CAT-03 — Product and variant data
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
- Product: brand, reference (unique per brand, e.g.
HC-X14), name, category, description (plain text, at most 5 000 characters), free characteristics (label/valuepairs, 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). - 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 limitsminQty(≥ 1),qtyStep(≥ 1) and optionalmaxQtyPerOrder(≥minQty), weight, status. Two variants of one product cannot carry exactly the same attribute values (409 DUPLICATE_VALUE). - A new variant gets stock rows at 0 in every active warehouse; the last active variant of an
ACTIVEproduct cannot be hidden, archived or deleted (409 VARIANT_LAST_ACTIVE). - Writing any price field needs
pricing.updatein addition tocatalog.update(403 PERMISSION_DENIEDwith the refused fields); the three prices must be orderedpriceSuperGros ≤ priceDemiGros ≤ priceDetail(422 PRICE_ORDER_INVALID). - The editor (BO-28) shows a completeness indicator listing what blocks publication (CAT-04 R2).
- Every change writes
AuditLog; price changes also writePriceChangerows (AUD-02).
Acceptance criteria
- Creating product
HC-X14twice for HOCO returns409 DUPLICATE_VALUE; the same reference under ACEFAST is accepted. - A variant with barcode
6931474761234already used returns409 DUPLICATE_VALUE. - Prices 35 000 / 32 000 / 39 000 (super-gros above demi-gros) return
422 PRICE_ORDER_INVALID. - A stock manager without
pricing.updatesaving a price gets403 PERMISSION_DENIEDnamingpriceDetail; 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
- Screens: BO-27 Products list · BO-28 Product editor
- API:
admin.products.listGET /admin/products·admin.products.createPOST /admin/products·admin.products.getGET /admin/products/{productId}·admin.products.updatePATCH /admin/products/{productId}·admin.variants.crudPOST · PATCH · DELETE /admin/products/{productId}/variants[/{variantId}]
CAT-04 — Product lifecycle
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
- Transitions (Lifecycles › Product):
DRAFTorHIDDEN→ACTIVE(publish);ACTIVEorOUT_OF_STOCK→HIDDEN; any →ARCHIVED;ARCHIVED→HIDDEN(restore,catalog.administer).ACTIVE↔OUT_OF_STOCKis automatic only:OUT_OF_STOCKwhen every active variant's available stock is 0, back toACTIVEon restock (receipt, return reception, cancellation). Any other move answers409 PRODUCT_TRANSITION_NOT_ALLOWED. - 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— otherwise422 PRODUCT_INCOMPLETEwithdetails.missing. The first publication setspublishedAt(drives Nouveautés). - Resellers, guests and delegates see
ACTIVEandOUT_OF_STOCKproducts only;OUT_OF_STOCKproducts show « En rupture » and cannot be added to a cart.DRAFT,HIDDENandARCHIVEDnever appear and their detail answers404 NOT_FOUND. - 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. - Every status change is audited with its reason; the automatic changes have the system as actor.
| Value | UI label (fr) | Meaning |
|---|---|---|
DRAFT | Brouillon | Being prepared; invisible to resellers. |
ACTIVE | Actif | Visible and orderable (subject to visibility rules). |
HIDDEN | Masqué | Kept but invisible to resellers. |
OUT_OF_STOCK | En rupture | Set automatically when every variant's available stock is 0; back to ACTIVE on restock. |
ARCHIVED | Archivé | Retired; remains referenced by past orders. |
Acceptance criteria
- Publishing a product without image returns
422 PRODUCT_INCOMPLETEwithmissing: ["images"]. to: OUT_OF_STOCKby a user returns409 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_FOUNDand a cart holding it showsPRODUCT_UNAVAILABLE.
Data — Product (status, publishedAt), ProductVariant, StockLevel, AuditLog.
Interfaces
- Screens: BO-27 Products list
- API:
admin.products.setStatusPOST /admin/products/{productId}/status
CAT-05 — Visibility per segment or reseller
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
- A product's audience mode is
EVERYONE(default),INCLUDE_ONLY(only the listed segments and resellers) orEXCLUDE(everyone except them), stored asProductVisibilityRulerows (admin.products.visibility,catalog.update).INCLUDE_ONLYorEXCLUDEwith empty lists, orEVERYONEwith lists, answers400 VALIDATION_FAILED. - A reseller matches a rule through its segments (CRM-06) or its id; a reseller both included and excluded is excluded.
- Applied everywhere: catalog lists, counts and facets, search suggestions, product detail (
404 NOT_FOUNDwhen 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. - Guests see only
EVERYONEproducts (AUTH-10). - A visibility change applies at the next read; a cart line that is no longer visible becomes
PRODUCT_UNAVAILABLE. AuditUPDATEwith the audience before and after.
Acceptance criteria
- A product in
INCLUDE_ONLYfor 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_UNAVAILABLEat the nextcart.get.
Data — ProductVisibilityRule, Segment, ResellerSegment, Product.
Interfaces
- Screens: BO-28 Product editor
- API:
catalog.products.listGET /catalog/products·admin.products.visibilityPUT /admin/products/{productId}/visibility
CAT-06 — Product import and export with a validation report
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
- Import (BO-32,
admin.products.import,catalog.administer): upload the file (IMPORT_FILE), runDRY_RUN, read the row-by-row report (VALID,WARNING,ERRORwith codes), thenAPPLYthe dry run within 24 hours (409 IMPORT_DRY_RUN_EXPIREDafter;409 IMPORT_ALREADY_APPLIEDtwice;422 IMPORT_DRY_RUN_REQUIREDif it failed). - 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, oneattr:<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 needpricing.update. - 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_INVALIDwithdetails.maxRowsormissingColumns).APPLYwrites valid and warning rows in batches of 200, upserting on SKU; rows that became invalid areSKIPPED. - Imported prices write
PriceChangerows with sourceIMPORT; stock is not imported here (ADM-06). - 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 ofpricing.view. The requester is notified when the file is ready. - Both are journaled (
IMPORT,EXPORT).
Acceptance criteria
- A 300-row file with 4 duplicate SKUs gives a dry run with 296
VALIDrows and 4ERRORrows naming the SKU; applying writes 296 variants. - A file of 6 000 rows returns
422 IMPORT_FILE_INVALIDwithmaxRows: 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
PriceChangerow. - A stock manager's export has no purchase price column.
Data — ImportJob, ExportJob, File, Product, ProductVariant, VariantAttributeValue, PriceChange, AuditLog.
Interfaces
- Screens: BO-32 Product import wizard
- API:
admin.products.importPOST /admin/imports/products·admin.products.exportPOST /admin/products/export·admin.imports.getGET /admin/imports/{importId}
CAT-07 — Catalog browsing and search
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V1 | MUST | cahier §5.1 | RA · RW · DA · API | SRV-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
- 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. - Filters are those of CAT-12; sorting:
relevance(default withq: 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_descon the caller's effective TTC unit price. - The tabs of reference screen 3 map to Nouveautés (
new,newest), Populaires (popular) and Promotions (promotion); category chips setcategoryId. - 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
relevanceandpopular. - Each card carries name, brand, image, the caller's price (
nullfor guests), promotion price and label, availability (IN_STOCK/OUT_OF_STOCK), Nouveauté badge and favourite flag. - A delegate passes the
resellerIdof a reseller in scope to see that reseller's prices and visibility (403 OUT_OF_SCOPEotherwise). - 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.
- 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-X14returns that product first; a barcode returns its product. - A guest sending
sort=price_ascgets400 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
resellerIdoutside their scope gets403 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
- Screens: RA-10 Search · RA-12 Catalog · RW-03 Catalog with advanced sidebar filters & search
- API:
catalog.products.listGET /catalog/products·catalog.products.suggestGET /catalog/products/suggest
CAT-08 — Product detail
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
catalog.products.getreturns the gallery, name, brand, reference, category path, description, characteristics, warranty, variants (name, attributes, the caller's price, availability, limitsminQty/qtyStep/maxQtyPerOrder, quantity already in the cart) and the active promotion with its end time.- Price: the caller's customer-type price with HT, TTC and VAT, and the recommended retail price (« Prix conseillé ») — both
nullfor 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). - The quantity stepper starts at
minQty, moves byqtyStepand stops atmaxQtyPerOrder(« 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). - « 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.
- 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.
- 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. - 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-N32for 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
- Screens: RA-14 Product detail · RA-15 Full specifications · RW-04 Product detail
- API:
catalog.products.getGET /catalog/products/{productId}·catalog.products.similarGET /catalog/products/{productId}/similar
CAT-09 — Favorites
Statement — The system should let a reseller keep a list of favourite products shared between the app and the web.
Rules
- 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).
favorites.addandfavorites.removeare idempotent (adding twice keeps one); at most 500 favorites.favorites.listreturns 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.- 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
- Screens: RA-16 Favorites · RW-05 Favorites
- API:
favorites.listGET /favorites·favorites.addPUT /favorites/{productId}·favorites.removeDELETE /favorites/{productId}
CAT-10 — Barcode scan
Statement — The system could let a reseller find a product by scanning its barcode with the phone camera.
Rules
- The scanner (RA-11,
mobile_scanner) reads EAN-13, EAN-8, UPC-A and Code 128 and callscatalog.products.byBarcodewith 8–14 digits (400 VALIDATION_FAILEDotherwise). - A match opens the product page on the scanned variant, with the caller's price (none for guests) and limits.
- 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. - 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
6931474761234opens 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
- Screens: RA-11 Barcode scanner
- API:
catalog.products.byBarcodeGET /catalog/products/by-barcode/{barcode}
CAT-11 — Home merchandising
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
catalog.homeaggregates: 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).- Popular categories: the 8 categories with the most units ordered over the last 30 days among products visible to the caller.
- 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.
- 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).
- 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
- Screens: RA-09 Home · RW-02 Home
- API:
catalog.homeGET /catalog/home
CAT-12 — Advanced combinable filters with counts
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V1 | MUST | Team decision (17 Oct 2026) | RA · RW · API | SRV-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
- Filters (shared by
catalog.products.listandcatalog.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 rangesattr.power_w=20..65, open ends10000..),availability,promotion,new. - 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.
- 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. - Unknown attribute keys return
400 VALIDATION_FAILED(UNKNOWN_ATTRIBUTE), inverted rangesINVALID_RANGE, price filters from a guestSIGN_IN_REQUIRED. - 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.
- Facets answer within the same performance budget as the list (CAT-07).
Acceptance criteria
- Selecting Chargeurs, brand HOCO and
attr.power_w=20..65returns only chargers of 20 to 65 W; the brand facet still shows ACEFAST with its count. attr.connector=USB_C&attr.connector=LIGHTNINGreturns 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.facetsGET /catalog/facets
CAT-13 — Filterable attributes per category
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V1 | MUST | Team decision (17 Oct 2026) | BO · API | SRV-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
- An
AttributeDefinitionhas an immutable snake_case key (power_w,connector), labels in fr/ar/en, an immutable type (NUMBERwith unit and 0–2 decimals,ENUMwith 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), arequiredToPublishflag, a sort order and a status.TEXTattributes cannot be filterable; incompatible widgets answer400 VALIDATION_FAILED. - 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.
- Each variant stores a
VariantAttributeValueper applicable attribute; a value of the wrong type or an unknown ENUM code answers400 VALIDATION_FAILED; publishing a product lacking a required value answers422 PRODUCT_INCOMPLETE(CAT-04). - BO-71 manages definitions (
admin.attributes.crud,catalog.*). Removing an ENUM code in use, or deleting a definition with values, answers409 RESOURCE_IN_USE(archive instead — the filter disappears from the apps, the values are kept). A newrequiredToPublishapplies to future publications; active products lacking the value are listed as incomplete but stay published. - 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"returns400 VALIDATION_FAILED. - Deleting
connectorwhile variants use it returns409 RESOURCE_IN_USE; archiving it removes the filter and keeps the values. - Marking
power_wrequired makes publishing a charger without power return422 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.facetsGET /catalog/facets·admin.attributes.crudGET · POST · PATCH · DELETE /admin/attribute-definitions[/{attributeId}]
Error codes
| Code | HTTP | Raised when |
|---|---|---|
DUPLICATE_VALUE | 409 | Brand or category name, product reference (per brand), SKU, barcode, attribute key or variant attribute combination already used |
RESOURCE_IN_USE | 409 | Deleting a used category or attribute, changing an ordered SKU, deleting a variant with stock |
VARIANT_LAST_ACTIVE | 409 | Hiding or deleting the last active variant of an active product |
PRODUCT_TRANSITION_NOT_ALLOWED | 409 | A product status move outside the lifecycle (e.g. setting OUT_OF_STOCK by hand) |
IMPORT_DRY_RUN_EXPIRED · IMPORT_ALREADY_APPLIED | 409 | Applying an old or already applied dry run |
CATEGORY_DEPTH_EXCEEDED · CATEGORY_CYCLE | 422 | Tree deeper than two levels or a cycle |
PRODUCT_INCOMPLETE | 422 | Publishing without the minimum data or required attributes |
PRICE_ORDER_INVALID | 422 | Prices not ordered super-gros ≤ demi-gros ≤ détail |
IMPORT_FILE_INVALID · IMPORT_DRY_RUN_REQUIRED | 422 | Unreadable file, missing columns, over 5 000 rows; apply without a successful dry run |
OUT_OF_SCOPE | 403 | A delegate's resellerId outside their scope |
Field codes in 400 VALIDATION_FAILED | 400 | SIGN_IN_REQUIRED, UNKNOWN_ATTRIBUTE, INVALID_RANGE, INVALID_BARCODE, TOO_SHORT |
Resellers & prospects (CRM)
Reseller and prospect records, creation paths, unique normalised phone, the ten-status lifecycle, assignment, segments, timeline, follow-ups, validation, documents, list, map and the 360° page.
Pricing
Three selling prices per variant chosen by the reseller's customer type, purchase and recommended prices with their history, VAT and totals, re-validation at cart and checkout, and the V2 per-reseller agreements.