HOCO B2B
DesignScreen specificationsBack-office

Catalog and stock

Page specs BO-27 to BO-37 and BO-71: products, the product editor with the three customer-type prices and attributes, brands, categories, the price grid, the import wizard, stock pages and attribute definitions.

The catalog pages manage the HOCO, ACEFAST and BOROFONE ranges (cahier §4.3). The stock pages control availability. Three binding decisions shape them:

  • Three selling prices per variant, one per customer type (§1b.4 in AUTHORING; PRC-01):
    • priceSuperGros (Super-gros), priceDemiGros (Demi-gros) and priceDetail (Détail), in HT centimes;
    • plus vatRate, the recommended retail price recommendedPrice, and the restricted purchasePrice;
    • the reseller's customer type selects the price, then promotions apply (PRC-02). V1 has no price lists and no quantity tiers. Per-reseller agreements are V2 (PRC-03).
  • Per-category attributes (power, connector, cable length, colour, capacity, Bluetooth version…) are defined in BO-71 and valued on every variant. They feed the reseller filters (CAT-12, CAT-13).
  • Stock quantities are visible only here. Resellers and guests see En stock or En rupture, never a number (STK-07). Cancellations and return receptions put quantities back automatically (STK-03).
IDScreenSectionTemplateReleaseDesignBuild
BO-27Products listCatalogtemplateV1Day 17DSH-04
BO-28Product editorCatalogbespokeV1Day 17DSH-04
BO-29BrandsCatalogtemplateV1Day 17DSH-04
BO-30CategoriesCatalogtemplateV1Day 17DSH-04
BO-31Price grid — three customer-type prices (bulk edit)CatalogtemplateV1Day 17DSH-04
BO-32Product import wizardCatalogbespokeV1Day 17DSH-04
BO-71Product attributes (filters) per categoryCatalogtemplateV1Day 17DSH-04
IDScreenSectionTemplateReleaseDesignBuild
BO-33Stock overviewStocktemplateV1Day 17DSH-05
BO-34Stock movements journalStocktemplateV1Day 17DSH-05
BO-35Stock receipt / adjustmentStocktemplateV1Day 17DSH-05
BO-36Inventory sessionsStocktemplateV1Day 17DSH-05
BO-37WarehousesStocktemplateV1Day 17DSH-05

BO-27 — Products list

FieldValue
TemplateList
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-04
Route/catalog/products
RequirementsCAT-03 · CAT-04 · CAT-13 · ACL-04
Permissions requiredcatalog.view · catalog.create (« Nouveau produit ») · catalog.update (status bulk actions) · catalog.export · catalog.administer (« Importer ») · pricing.view (purchase price and margin columns) · stock.view (stock columns)

Purpose. Browse and maintain the catalog: find a product by name, reference or barcode, see its status, prices, stock and attribute completeness, and change statuses in bulk.

Who uses it. Stock manager (owner of the catalog), super admin. Commercial admin, finance and direction read it.

Entry points. Catalogue › Produits, global search, and the best-seller and stock cards of the dashboards.

Layout.

  • Header: « Produits » with the count. Actions: « Nouveau produit » (BO-28), « Importer » (BO-32), « Exporter ».
  • Tabs:
    • « Tous ».
    • « Actifs » (ACTIVE).
    • « Brouillons » (DRAFT).
    • « Masqués » (HIDDEN).
    • « En rupture » (OUT_OF_STOCK).
    • « Archivés » (ARCHIVED).
    • « À compléter »: products with a missing required attribute value, image or price.
ColumnSource fieldSortableDefault visible
ImagemainImage.thumbnailUrlNoYes
ProduitnameYesYes
Référencereference (mono)YesYes
Marquebrand.nameYesYes
Catégoriecategory.path (« Câbles › USB-C »)YesYes
VariantesvariantsCountYesYes
Prix Détail HTpriceDetail of the default variant (range « 245,00 – 290,00 DA » when variants differ)YesYes
Prix Demi-gros HT / Prix Super-gros HTpriceDemiGros / priceSuperGros (same display)YesNo
Prix d'achat HTpurchasePrice (needs pricing.view)YesNo
Marge Détail(priceDetail − purchasePrice) ÷ priceDetail (needs pricing.view)YesNo
Stock disponibleΣ available over variants and warehouses (needs stock.view)YesYes
Vu par les revendeursIN_STOCK / OUT_OF_STOCK (StockAvailability pill)NoYes
Statutstatus (ProductStatus)YesYes
AttributsCompleteness « 4/5 » (required attribute values filled on every active variant)YesYes
Visibilité« Tous » or « Restreinte » (rules exist)NoNo
Promo activeBadge with the promotion nameNoNo
Mis à jourupdatedAtYesNo
  • Filters:
    • q: name, reference, SKU or barcode.
    • brand, category (tree; includes sub-categories), status.
    • stock = in / low / out (needs stock.view).
    • incomplete = attributes / images / prices.
    • visibility=restricted, promo=true, updatedFrom / updatedTo.
  • Default sort: updatedAt descending.
  • Bulk actions: « Publier », « Masquer », « Archiver » (admin.products.setStatus, item by item with the progress report), « Changer de catégorie », « Exporter la sélection ».
  • Row actions: « Modifier », « Dupliquer », « Voir le stock » (BO-33 filtered), « Historique des prix » (needs pricing.view).

Data. admin.products.list, admin.products.setStatus, admin.products.update, admin.products.export.

Actions.

  • « Publier »: « Publier 12 produits ? Ils deviennent visibles et commandables par les revendeurs autorisés. ». Products failing the publish checks (see BO-28) are skipped and listed with their reason.
  • « Archiver »: « Archiver 3 produits ? Ils restent visibles dans les commandes passées. ».
  • Each status change is audited STATUS_CHANGE.

Rules.

  • OUT_OF_STOCK is set automatically when every variant's available stock is 0, and returns to ACTIVE on restock. It cannot be chosen by hand (CAT-04).
  • Without pricing.view, the purchase-price and margin columns are absent from the column menu and from the API response (ACL-04).
  • Without stock.view, only the Vu par les revendeurs column is shown, never quantities.

Real-time. stock.alert refreshes the stock columns of visible rows.

States. First use: « Le catalogue est vide », with « Nouveau produit » and « Importer un fichier ».

Acceptance criteria.

  • Searching a barcode returns the product of the matching variant.
  • The « À compléter » tab lists exactly the products whose active variants miss a required attribute value, an image or one of the three prices.
  • A commercial admin's response contains no purchasePrice.
  • Bulk « Publier » on 10 products, two of them without an image, publishes 8 and reports the 2 with « Image principale manquante ».

BO-28 — Product editor

FieldValue
TemplateBespoke (Form skeleton with sections and a variants grid)
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-04
Route/catalog/products/new · /catalog/products/[productId] (?section=)
RequirementsCAT-03 · CAT-05 · PRC-01 · CAT-13 · CAT-04 · PRC-04 · STK-07 · ACL-04
Permissions requiredcatalog.view (read-only editor) · catalog.create (new) · catalog.update (edit, variants, the three selling prices, attribute values, visibility, status) · pricing.view (see purchase price, margins, price history) · pricing.update (edit the purchase price) · stock.view (stock section) · stock.update (alert thresholds)

Purpose. Create and maintain one product with everything resellers and the back-office need (cahier §4.3, §15.4):

  • identity and description, technical characteristics and images;
  • variants with SKU, barcode and attribute values;
  • the three selling prices, VAT, recommended and purchase price, and quantity limits;
  • visibility by segment or reseller;
  • stock and alert thresholds, and the status.

Who uses it. Stock manager, super admin. Others read it.

Entry points. BO-27 (row, « Nouveau produit », « Dupliquer »), the global search, and the import report (« Ouvrir le produit »).

Layout

  • Header:
    • Main image, name, reference, brand, the ProductStatus pill and the « Vu par les revendeurs » pill (En stock or En rupture).
    • Primary action by status:
      • « Publier » (DRAFT / HIDDEN → ACTIVE);
      • « Masquer » (ACTIVE → HIDDEN).
    • « Plus » menu: « Archiver », « Dupliquer », « Historique ».
  • Left anchor navigation: Général, Caractéristiques, Images, Variantes et prix, Attributs, Visibilité, Stock, Historique.
  • Sticky save bar, as in the Form template.

Général.

FieldTypeValidationDefaultHelp text / permission
MarqueSelect (HOCO, ACEFAST, BOROFONE)Required——
CatégorieTree select (category ▸ sub-category)Required; must be a leaf—Determines the attributes to fill. Changing it asks to remap attribute values.
NomTextRequired, 3–120 characters—Shown to resellers
RéférenceText, uppercaseRequired, unique (409), 2–40 characters A–Z 0–9 - _—« Référence fabricant, ex. X14 »
DescriptionRich text (bold, lists, links only)5 000 characters max——
GarantieNumber of months0–366Assumption — default to confirm
Taux de TVASelect: 0 %, 9 %, 19 %Required19 % (vatRate 1900)Applies to every variant (PRC-04)
Nouveauté jusqu'auDateOptionalCreation + 30 daysDrives the « new arrivals » filter

Caractéristiques. A free key/value list for the technical sheet: label, 60 characters max; value, 200 characters max; drag to reorder; 40 rows max. Attribute values (see Attributs) are added to the sheet automatically, so they are not repeated here.

Images.

  • Up to 10 images (PRODUCT_IMAGE: public, 5 MB max, JPEG/PNG/WebP). Recommended square format, 1 200 px or more.
  • Drag to reorder. The first image is the main one.
  • Optional per-variant image (e.g. the colour).
  • Uploads run through files.createUpload → PUT → files.completeUpload. Thumbnails are generated by the server.

Variants and prices

A grid with one row per variant, inline editing, and « Ajouter une variante ». Each column below is a variant field:

FieldTypeValidationDefaultPermission / help text
Nom de la varianteTextRequired when there is more than one variant, 60 characters maxBuilt from the variant attributes (« Noir · 1 m »)—
SKUTextRequired, unique across the catalog<référence>-<n>—
Code-barresTextOptional; EAN-13 or UPC-A with a valid check digit; unique—Scanner input accepted
Prix Super-gros HTMoney (DA)Required, above 0—catalog.update. TTC is shown below in grey.
Prix Demi-gros HTMoney (DA)Required, above 0—catalog.update
Prix Détail HTMoney (DA)Required, above 0—catalog.update
Prix conseillé TTCMoney (DA)Optional; must be at least the Détail TTC (warning only)—The recommended retail price shown to resellers (CAT-08)
Prix d'achat HTMoney (DA)Optional, above 0—Rendered only with pricing.view; editable only with pricing.update (ACL-04)
MargesRead-only: % per type, (price − purchase) ÷ price——Only with pricing.view. Danger tone when negative.
Quantité minimumInteger1 or more1Per order line (ORD-02)
Multiple deInteger1 or more; the minimum must be a multiple1« Vendu par lots de… »
Maximum par commandeIntegerOptional; at least the minimum——
ActiveToggleAt least one active variant to publishOn—

Price rules:

  • Order of the three prices: priceSuperGros ≤ priceDemiGros ≤ priceDetail. A violation blocks saving with « Le prix Super-gros doit être inférieur ou égal au prix Demi-gros, lui-même inférieur ou égal au prix Détail. » Assumption — the strict ordering is to confirm with HOCO.
  • Display: prices are entered HT. The grid shows the TTC value under each price (price × (1 + vatRate), rounded to the centime).
  • Who pays which price: a reseller pays the price of their customer type, then promotions (PRC-02). Guests see no price (AUTH-10).
  • History: every change of a selling or purchase price writes a PriceChange (before, after, user, time). « Historique des prix » (needs pricing.view) opens a drawer per variant with a line chart of the three prices and the change table (admin.variants.priceHistory).
  • Effect on orders: a price change affects carts re-priced after the change. Submitted orders keep their prices.
  • No price lists, no tiers in V1: no quantity-tier editor and no price-list column. Per-reseller agreements are V2 (PRC-03).

Attributes

  • Grid: one row per variant, one column per attribute definition of the product's category, including those inherited from the parent category (BO-71).
  • Inputs by attribute type:
    • Number: numeric input with its unit (« 20 W », « 1 m », « 10 000 mAh »), checked against the min and max.
    • Single-choice list: a select of the allowed values (Connecteur: USB-C, Lightning, Micro-USB).
    • Multiple-choice list: chips (Ports : USB-A, USB-C).
    • Yes / no: a toggle (Charge rapide).
  • Required attributes are marked « * ». An empty required value blocks publication, and the cell shows « À renseigner ».
  • « Appliquer à toutes les variantes » copies a value across the column.
  • Values are saved with the variants through admin.variants.crud as VariantAttributeValue rows.
  • They feed the reseller filters and facets (catalog.facets) and the product sheet.

Visibilité (CAT-05):

  • Mode: « Visible par tous les revendeurs » (default), « Visible uniquement pour… » (segments and/or resellers), or « Masqué pour… » (segments and/or resellers).
  • Search pickers select the segments and resellers. The rule is saved with admin.products.visibility.
  • Guests see products that are visible to all.

Stock (needs stock.view; back-office only):

  • Read-only table per variant × warehouse: Physique, Réservé, Disponible, Seuil d'alerte (editable with stock.update through admin.stock.thresholds), Vu par les revendeurs.
  • Links: « Mouvements » (BO-34 filtered) and « Nouvelle réception » (BO-35).
  • The hint « Les revendeurs voient uniquement En stock / En rupture » sits above the table.

Historique. Audit entries of the product (create, update with changed fields, status changes, visibility changes) and price changes.

Complementary products and new-arrival flag

Change request 03 (NOT-15, NOT-11, DSH-14).

  • Section « Produits complémentaires »: a product search picker that adds up to 5 products (no self-reference, no duplicate), ordered by drag-and-drop (ProductComplement.position). Example: Coque iPhone 15 → Verre trempé iPhone 15. The first complement that is in stock and visible to the reseller is the one suggested the day after an order.
  • Checkbox « Annoncer comme nouveauté » (on by default) in Publication: at first publication the product enters the next smart.newArrivalsDigest run. It has no effect once publishedAt is set.

Actions

ActionEndpointConfirmation (FR)OptimisticAuditNotifies
« Créer le produit »admin.products.create (draft + default variant)—NoCREATE—
« Enregistrer »admin.products.update + admin.variants.crud for changed variants (with version)When a selling price changes on an ACTIVE product: « 3 prix modifiés. Ils s'appliqueront aux prochains paniers. »NoUPDATE + PriceChange rows—
« Publier »admin.products.setStatus (ACTIVE)« Publier ce produit ? Il devient visible dans les applications. »NoSTATUS_CHANGE—
« Masquer »admin.products.setStatus (HIDDEN)« Masquer ce produit ? Il disparaît du catalogue ; les paniers qui le contiennent afficheront un avertissement. »NoSTATUS_CHANGE—
« Archiver »admin.products.setStatus (ARCHIVED)« Archiver ce produit ? Il reste visible dans les commandes passées et ne peut plus être commandé. »NoSTATUS_CHANGE—
Delete a variantadmin.variants.crud (DELETE)« Supprimer la variante Noir · 1 m ? » A variant referenced by orders is deactivated instead.NoDELETE / UPDATE—
Save the visibility rulesadmin.products.visibility—NoUPDATE—
Set alert thresholdsadmin.stock.thresholds—YesUPDATE—

Rules

  • Publish checks, all required:
    • a brand and a leaf category;
    • at least one image;
    • at least one active variant;
    • the three prices set (above 0) on every active variant;
    • every required attribute filled on every active variant (CAT-13). Failures are listed in a dialog with links to the fields.
  • Statuses (CAT-04): DRAFT, ACTIVE, HIDDEN and ARCHIVED are manual. OUT_OF_STOCK is automatic. An archived product can be duplicated, not re-published. Assumption — confirm.
  • Purchase price: never sent to users without pricing.view. A write without pricing.update is refused (403 PERMISSION_DENIED, details.field = "purchasePrice").
  • Category change: values of attributes that do not exist in the new category are dropped after confirmation (« 2 attributs ne s'appliquent pas à la catégorie Écouteurs et seront supprimés »).
  • Concurrency: the product carries version. A 409 VERSION_CONFLICT shows the reload dialog.

Real-time. stock.alert for a variant of this product refreshes the Stock section.

States.

  • Read-only mode for users without catalog.update: every field is disabled and the save bar is hidden.
  • Uploading images: per-image progress.
  • Draft: banner « Brouillon — invisible pour les revendeurs ».
  • Archived: read-only banner.

Acceptance criteria

  • A product cannot be published while a required attribute or one of the three prices is missing on an active variant. The dialog lists each missing item.
  • Saving Super-gros 230,00 DA, Demi-gros 245,00 DA and Détail 290,00 DA HT records three PriceChange rows. A Demi-gros reseller's next cart shows 245,00 DA HT (291,55 DA TTC at 19 %), while a submitted order keeps its old price.
  • A commercial admin opening the editor sees the three selling prices, but no purchase price, no margin and no « Historique des prix ».
  • Attribute values entered here appear as filters with counts in the reseller catalog (catalog.facets) after publication.
  • The Stock section shows physical, reserved and available quantities, while the reseller app shows only En stock or En rupture for the same variant.

BO-29 — Brands

FieldValue
TemplateList (with drawer)
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-04
Route/catalog/brands
RequirementsCAT-01
Permissions requiredcatalog.view · catalog.create · catalog.update · catalog.delete

Purpose. Manage the brands as data: HOCO, ACEFAST, BOROFONE are seeded (cahier §4.3).

Who uses it. Stock manager.

Entry points. Catalogue › Marques.

Layout.

  • Table: logo, Nom, Produits (active / total), Ordre, Actif.
  • Drawer:
    • Nom: required, unique.
    • Logo: BRAND_LOGO, public, SVG/PNG.
    • Description: 300 characters max.
    • Ordre d'affichage.
    • Actif.

Data. admin.brands.crud.

Actions. Create and edit (audited). « Supprimer » archives the brand when products reference it: « Cette marque est utilisée par 214 produits : elle sera archivée, pas supprimée. »

Rules. An archived brand disappears from reseller filters, and its products stay unchanged.

Real-time. None.

States. The three seeded brands are always present.

Acceptance criteria.

  • Deleting a referenced brand archives it.
  • Changing the display order changes the brand order in the reseller filters.

BO-30 — Categories

FieldValue
TemplateList (tree variant, with drawer)
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-04
Route/catalog/categories
RequirementsCAT-02 · CAT-12
Permissions requiredcatalog.view · catalog.create · catalog.update (edit, reorder) · catalog.delete

Purpose. Maintain the category tree of categories and sub-categories (chargers, cables, earphones, power banks, car holders, audio, watches, accessories; cahier §4.3). It drives catalog navigation, the category and sub-category filters, and which attributes apply.

Who uses it. Stock manager.

Entry points. Catalogue › Catégories.

Layout.

  • Tree: drag to reorder and to re-parent, two levels. Each node shows the name, active products count, attribute definitions count (link « Attributs » → BO-71 for that category), and an active toggle.
  • Drawer:
    • Nom: required, unique among siblings.
    • Catégorie parente: none for a top-level category.
    • Image / icône: public.
    • Ordre.
    • Actif.

Data. admin.categories.crud, admin.categories.reorder.

Actions.

  • Reorder is optimistic (rolled back on error) and audited UPDATE.
  • Re-parenting a category with products asks: « Déplacer Câbles USB-C sous Accessoires ? Les attributs hérités de Câbles ne s'appliqueront plus à ses 48 produits. »
  • Delete is refused while products or sub-categories exist: move them first.

Rules.

  • Depth: two levels, category and sub-category. Assumption — confirm.
  • Products sit on leaf categories only.
  • Inactive categories: an inactive category hides its products from navigation and filters, but not from search by reference.

Real-time. None.

States. The seeded top-level categories are listed on first use.

Acceptance criteria.

  • Moving a sub-category updates the reseller category filters on the next catalog request.
  • A category with products cannot be deleted.

BO-31 — Price lists & agreements

FieldValue
TemplateList (editable grid)
ReleaseV1 (three-price grid). Per-reseller agreements are V2.
Design dayDay 17 — DES-17
Build taskDSH-04
Route/catalog/prices
RequirementsPRC-02 · PRC-01 · PRC-03
Permissions requiredcatalog.view (page) · catalog.update (edit the three selling prices) · pricing.view (purchase price, margins, history) · pricing.update (purchase price)

Purpose. In V1, segment price lists are replaced by the three customer-type prices (§1b.4). This page is the price grid: review and change the Super-gros, Demi-gros and Détail prices of many variants at once, check margins, and follow price history. Per-reseller agreements will be added here in V2 (PRC-03).

Who uses it. Stock manager (price updates after a supplier change), finance manager (margins), super admin.

Entry points. Catalogue › Tarifs, and « Historique des prix » from BO-27 and BO-28.

Layout.

  • A banner: « Les accords tarifaires par revendeur arrivent en V2. En V1, chaque revendeur paie le prix de son type de client. »
  • A grid with one row per active variant (product name, SKU, brand, category) and inline-editable price cells:
ColumnSource fieldEditableDefault visible
Produit / varianteproduct.name, variant.name, skuNoYes
Marque, Catégorie—NoYes
Prix d'achat HTpurchasePriceWith pricing.update (only rendered with pricing.view)Yes, when permitted
Super-gros HTpriceSuperGrosWith catalog.updateYes
Demi-gros HTpriceDemiGrosWith catalog.updateYes
Détail HTpriceDetailWith catalog.updateYes
TVAvatRateNo (edited on the product)Yes
Prix conseillé TTCrecommendedPriceWith catalog.updateNo
Marge Super-gros / Demi-gros / DétailComputedNo (pricing.view)No
Dernière modificationLast PriceChange date and userNoYes
  • Edited cells are highlighted. The save bar shows « 46 prix modifiés sur 18 variantes · Annuler · Enregistrer ».
  • Bulk tools on the selection:
    • « Variation » (+/− % or amount on one or more of the three types);
    • « Recalculer à partir du Détail » (Demi-gros = Détail − x %, Super-gros = Détail − y %);
    • « Arrondir » (to the nearest 1, 5 or 10 DA). Each tool shows a before/after preview of the first rows.
  • Filters: q, brand, category, status, missing=price, marginBelow (needs pricing.view), changedSince.
  • History drawer per row (needs pricing.view): chart and table of PriceChange.
  • « Exporter » / « Importer les prix » link to BO-32 in « Prix uniquement » mode.

Data.

Actions. « Enregistrer » asks for confirmation: « Enregistrer 46 prix sur 18 variantes ? Ils s'appliqueront aux prochains paniers ; les commandes passées ne changent pas. ». The save is not optimistic. Each variant writes UPDATE and its PriceChange rows.

Rules.

  • Same price rules as BO-28: Super-gros ≤ Demi-gros ≤ Détail, all above 0. Violating cells are flagged before saving.
  • Rounding happens in centimes on the server. The preview shows the exact values.
  • Without catalog.update, the grid is read-only.

Real-time. None.

States.

  • No variant with a missing price: « Tous les prix sont renseignés » under the missing filter.
  • A save that fails part-way lists the variants that failed. The others stay saved.

Acceptance criteria.

  • A +5 % variation on Détail for brand ACEFAST, rounded to 5 DA, previews and then saves exactly the previewed values.
  • After saving, each changed variant has one PriceChange per changed price type.
  • A finance manager sees margins and history but cannot edit selling prices without catalog.update.

BO-32 — Product import wizard

FieldValue
TemplateBespoke (Form skeleton, four steps)
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-04
Route/catalog/products/import (?job= to resume)
RequirementsCAT-06 · CAT-13 · PRC-01 · ADM-06
Permissions requiredcatalog.administer · pricing.update (to import purchase prices; otherwise the column is ignored)

Purpose. Import or update products, variants, the three prices and attribute values from an Excel/CSV file, with a validation report before anything is written (cahier §4.3 « Import et export des produits via Excel/CSV », §13.2 initial import). It is used for the initial catalog load and for price updates. Stock quantities are not imported here: initial stock is the Stock import of BO-67.

Who uses it. Stock manager, super admin (initial load, LCH-01).

Entry points. « Importer » on BO-27, « Importer les prix » on BO-31, and Administration › Imports de données (BO-67, product type).

1 · Modèle

  • Type d'import:
    • « Produits et variantes » (create and update everything);
    • « Prix uniquement » (updates the three prices, the recommended price and, with pricing.update, the purchase price, matched by SKU).
  • Catégorie (optional): pre-builds the attribute columns of that category. Without one, the template holds all attribute keys.
  • « Télécharger le modèle » returns an XLSX with three sheets:
    • Produits: one row per variant (columns below);
    • Référentiel: brands, category paths, allowed values of list attributes. It feeds dropdowns in Produits.
    • Aide: the column rules, in French.
  • Mode: « Créer uniquement », « Mettre à jour uniquement », « Créer et mettre à jour » (default). The match key is the SKU.

2 · Fichier

  • Drop zone for .xlsx or .csv (UTF-8, ; or , separator, detected), 10 MB and 5 000 rows max. Assumption — limits to confirm. The file is uploaded as IMPORT_FILE.
  • Column mapping: file headers are matched to template columns automatically (case- and accent-insensitive). Unmatched columns are shown with a select « Ignorer / associer à… ».
  • A preview of the first 10 rows follows.

3 · Analyse (dry run)

  • admin.products.import with dryRun: true creates an ImportJob. The page polls admin.imports.get every 2 s (JobStatus QUEUED → RUNNING → SUCCEEDED / FAILED) and shows progress.
  • Report:
    • counters: Lignes, À créer, À mettre à jour, Inchangées, Erreurs, Avertissements;
    • a table of problems: row number, SKU, column, value, code and message (« Ligne 14 — Prix Demi-gros supérieur au prix Détail »);
    • filters: errors / warnings;
    • « Télécharger les lignes en erreur » (XLSX with an Erreur column, ready to fix and re-upload).
  • Price changes summary: number of prices raised or lowered, and the largest change in %. Changes over ±30 % are listed as warnings.

4 · Application

  • « Importer » is enabled when Erreurs = 0, or when « Ignorer les lignes en erreur » is ticked.
  • Confirmation: « Importer 1 182 lignes (240 créations, 942 mises à jour, 12 lignes ignorées) ? ».
  • The apply runs as a second job on the same file (dryRun: false), with progress.
  • Result: counts, « Voir les produits importés » (BO-27 filtered on the job), and the job in the import history.

Template columns. Fields of one product are repeated on each of its variant rows.

ColumnRequiredRule
marqueYes (products)Exact brand name from Référentiel
categorieYes (products)Leaf category path « Câbles › USB-C »
reference, nomYes (products)As in BO-28
description, garantie_mois, tvaNotva ∈ 0, 9, 19
skuYesMatch key; unique
code_barresNoEAN-13 / UPC-A, valid check digit
varianteWhen the product has several variantsVariant name
prix_super_gros_ht, prix_demi_gros_ht, prix_detail_htYes on createDA with up to two decimals; Super-gros ≤ Demi-gros ≤ Détail
prix_conseille_ttcNoDA
prix_achat_htNoIgnored with a warning without pricing.update
qte_min, multiple, qte_maxNoIntegers, as in BO-28
attr.<cle> (e.g. attr.puissance_w, attr.connecteur)Required attributes: yes, for rows to publishNumber, an allowed value, values separated by | for multiple choice, or oui / non
imagesNoPublic image URLs separated by |, downloaded by the server. Assumption — confirm the image source with HOCO.
statutNoDRAFT (default) or ACTIVE (publish checks apply)

Rules.

  • Dry run first: nothing is written by the analysis step. Apply runs only on an analysed file of the same content (file hash), so a changed file must be re-analysed.
  • Idempotent apply: applying the same file twice gives no duplicates; the second run reports everything « Inchangées ».
  • Prices: every price change writes a PriceChange with the import job as source.
  • Publication: rows with statut = ACTIVE that fail the publish checks are created as DRAFT, with a warning.
  • Audit: one IMPORT entry per apply, with the counts and the file id.
  • Permission gap: product import jobs are started with catalog.administer, but admin.imports.get is declared with settings.administer (contract notes).
  • Template: the dashboard serves it, generated from the import schema in packages/contracts. Assumption — a server endpoint per type and category would keep attribute columns exact.

Real-time. None. Progress comes from polling the job.

States.

  • Analysis failure (FAILED): « Analyse impossible : fichier illisible ou feuille Produits absente ».
  • Recovery: leaving the page keeps the job, and ?job= resumes at the right step.
  • Expired upload (after 24 h): « Fichier expiré — téléversez-le de nouveau ».

Acceptance criteria.

  • A file with a Demi-gros price above the Détail price is reported as an error on the right row and column, and cannot be applied unless that row is ignored.
  • Dry run writes nothing: product counts and prices are unchanged until « Importer ».
  • Re-applying the same file creates no product and reports every row as unchanged.
  • The error-rows download contains exactly the rows in error, with the message column.
  • Attribute values imported in attr.* columns appear in BO-28 and in the reseller filters.

BO-33 — Stock overview

FieldValue
TemplateList
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-05
Route/stock
RequirementsSTK-01 · STK-02 · STK-05 · STK-07
Permissions requiredstock.view · stock.update (thresholds) · stock.export · pricing.view (value column)

Purpose. Show physical, reserved and available stock per variant, globally or per warehouse, with alerts (cahier §4.3 « Stock global ou par entrepôt, stock réservé et seuil d'alerte »). This is the only place, with BO-28 and BO-07, where quantities are shown.

Who uses it. Stock manager. Delivery manager and commercial admin read it.

Entry points. Stock › État du stock, BO-07 cards, stock.alert toasts, and BO-27 « Voir le stock ».

Layout.

  • Toggle: « Global » / « Par entrepôt » (hidden when only one warehouse exists).
ColumnSource fieldSortableDefault visible
Produit / varianteName, SKU, imageYesYes
Marque, Catégorie—YesYes
Entrepôtwarehouse.name (per-warehouse view)YesYes
PhysiquephysicalYesYes
Réservéreserved (validated orders not yet shipped)YesYes
Disponibleavailable = physical − reservedYesYes
Seuil d'alertethreshold (inline edit with stock.update)YesYes
AlerteOUT / LOW / OK pillYesYes
Vu par les revendeursIN_STOCK / OUT_OF_STOCKNoYes
Couverture (jours)available ÷ average daily shipped quantity over 30 daysYesNo
Dernier mouvementlastMovementAt + typeYesNo
Valeurphysical × purchase price (needs pricing.view)YesNo
  • Filters: q, warehouse, brand, category, alert = OUT / LOW / OK, productStatus.
  • Default sort: alert (OUT, then LOW), then available ascending.
  • Header actions: « Réception » / « Ajustement » (BO-35), « Inventaire » (BO-36), « Définir les seuils » (bulk), « Exporter ».
  • Row click: quick-view drawer with the variant's last 20 movements and the orders holding its reservations (number, status, quantity).

Data. admin.stock.levels, admin.stock.thresholds, admin.stock.movements (drawer).

Actions.

  • Inline threshold edit is optimistic and audited UPDATE.
  • « Définir les seuils » on a selection asks « Appliquer un seuil de 20 aux 35 variantes sélectionnées ? ».
  • Export produces XLSX or CSV.

Rules.

  • Reservation happens at VALIDATED (the back-office confirmation) and deduction at SHIPPED (default settings, STK-03).
  • A cancellation releases the reservation (RESERVATION_RELEASE). A return reception re-enters stock (RETURN). Both are automatic.
  • Vu par les revendeurs is En stock when available is above 0, else En rupture. No other value and no quantity reaches resellers (STK-07).
  • An available stock below 0 is possible only after an authorised override at order confirmation (STK-08). It shows in danger with « Dérogation » and a link to the order.

Real-time. stock.alert updates the matching rows and the alert counters. order.status_changed refreshes the reserved quantities of visible rows (debounced 10 s).

States. « Aucune alerte » under alert=OUT. A variant without a stock level yet shows « 0 » with the hint « Aucun mouvement ».

Acceptance criteria.

  • Confirming an order of 5 units raises Réservé by 5 and lowers Disponible by 5. Cancelling it brings both back without any manual action.
  • A variant whose available stock reaches 0 shows En rupture in the reseller app at the next catalog request, and appears under alert=OUT here.
  • Valeur is absent for users without pricing.view.

BO-34 — Stock movements journal

FieldValue
TemplateList
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-05
Route/stock/movements
RequirementsSTK-04 · AUD-02 · STK-03
Permissions requiredstock.view · stock.export

Purpose. The immutable ledger of every stock variation (cahier §4.3 « Les variations de stock doivent être journalisées »).

Who uses it. Stock manager, finance (stock reconciliation), super admin.

Entry points. Stock › Mouvements, BO-33 drawer, BO-28 Stock, and links from orders, returns and inventory sessions.

Layout.

ColumnSource fieldSortableDefault visible
DateoccurredAtYes (default descending)Yes
Typetype (StockMovementType)YesYes
VarianteName + SKUYesYes
Entrepôtwarehouse.nameYesYes
QuantitéSigned quantity (+ success / − danger)YesYes
Stock physique aprèsphysicalAfterNoYes
RéférenceLinked source: CMD-… (reservation, release, shipment), RET-… (return reception), receipt document, inventory session, adjustmentNoYes
MotifreasonNoYes
ParUser, or « Système »YesYes
  • Filters: from / to, type, variant, warehouse, user, reference.
  • Export: XLSX / CSV.

Data. admin.stock.movements.

Actions. Read-only. Nothing is edited or deleted: a correction is a new adjustment (BO-35).

Rules.

  • Movements are written by the server only, in the same transaction as their cause (order status, return reception, receipt, adjustment, inventory validation).
  • RESERVATION and RESERVATION_RELEASE change the reserved quantity, not the physical one. The Stock physique après column marks them « — ».

Real-time. None.

States. A filter with no result shows « Aucun mouvement sur la période ».

Acceptance criteria.

  • Receiving a return of 3 units creates one RETURN movement of +3 that links to its RET- number.
  • Cancelling a confirmed order creates RESERVATION_RELEASE movements for each line.
  • No action on this page can modify a movement.

BO-35 — Stock receipt / adjustment

FieldValue
TemplateForm
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-05
Route/stock/operations/new?type=receipt or ?type=adjustment
RequirementsSTK-06 · STK-04
Permissions requiredstock.create (receipt) · stock.update (adjustment)

Purpose. Record goods received from suppliers, and adjust stock with a reason (breakage, loss, counting error) (cahier §3 « Responsable stock : quantités, inventaires et alertes »).

Who uses it. Stock manager.

Entry points. BO-33 header, BO-07 « Nouvelle réception », BO-28 Stock.

Layout.

  • Tabs « Réception » and « Ajustement ».
  • Header fields:
    • Entrepôt: required; the default warehouse is preselected.
    • Date: defaults to today; the past 30 days are allowed.
    • Référence document: supplier delivery note, for receipts.
    • Note.
  • Lines:
    • A variant search with barcode-scanner input: a scan adds the line, or increments it when already present.
    • Receipt line: Quantité reçue (integer, 1 or more).
    • Adjustment line: « Nouvelle quantité comptée » or « Écart (+/−) » (switch), showing current physical, resulting physical and difference, plus a required Motif (free text, 3–200 characters).
  • Summary bar: lines, total units, and for adjustments the units added and removed.

Data. admin.stock.receipt, admin.stock.adjust, admin.stock.levels (current quantities), admin.products.list (search).

Actions.

  • Receipt: « Enregistrer la réception de 12 lignes (340 pièces) dans Dépôt Oran ? » This creates RECEIPT movements, audited CREATE.
  • Adjustment: « Appliquer 4 ajustements (+6 / −11 pièces) ? » This creates ADJUSTMENT movements with their reasons, audited CREATE.
  • Both use an Idempotency-Key, so a double click never posts twice.

Rules.

  • Adjustments cannot bring physical stock below the reserved quantity without a warning (« 3 pièces sont réservées par des commandes confirmées »).
  • An adjustment larger than 10 % of the physical stock is flagged for review in the summary. Assumption — no extra approval in V1.
  • Receipts and adjustments may move a product from OUT_OF_STOCK back to ACTIVE automatically.

Real-time. None.

States. An unknown barcode shows « Code-barres inconnu » with « Créer le produit ». Leaving with lines entered is guarded.

Acceptance criteria.

  • Scanning the same barcode twice gives one line with quantity 2.
  • A receipt of 50 units of an out-of-stock variant makes it En stock for resellers immediately after saving.
  • An adjustment without a reason cannot be saved.

BO-36 — Inventory sessions

FieldValue
TemplateList (with a session detail)
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-05
Route/stock/inventories · /stock/inventories/[sessionId]
RequirementsSTK-06 · STK-04
Permissions requiredstock.update (open, count, review; the contract also gates reading with it) · stock.validate (validate)

Purpose. Run physical counts for a warehouse or part of it, review differences, and post the adjustments in one validated step.

Who uses it. Stock manager, with warehouse staff on a tablet.

Entry points. Stock › Inventaires (badge = open sessions), BO-33 « Inventaire ».

Layout.

  • List: Nom, Entrepôt, Périmètre (all, brand or category), Ouverte le / par, Lignes comptées / attendues, Écarts, État.
  • State labels: « Ouverte », « En revue », « Validée », « Annulée ». Assumption — the contract has no inventory-status enum yet.
  • Session detail:
    • Header: name, warehouse, scope, state.
    • Count grid:
      • variant and SKU;
      • Attendu: snapshot at opening, hidden in « comptage à l'aveugle » mode;
      • Mouvements depuis l'ouverture;
      • Compté (input; a scanner adds +1);
      • Écart, and its value with pricing.view.
    • Filters « Non comptés », « Avec écart ». Large touch targets for tablet use.
  • Actions: « Passer en revue », « Valider l'inventaire », « Annuler ».

Data. admin.inventory.crud (sessions and lines), admin.inventory.validate.

Actions.

  • Count entry autosaves every line (optimistic, with a « Enregistré » tick).
  • « Valider l'inventaire » (stock.validate): « Valider l'inventaire Dépôt Oran — Câbles ? 23 écarts seront passés en mouvements d'inventaire (+14 / −31 pièces). ». This posts INVENTORY movements, audited VALIDATE.

Rules.

  • Posted adjustment = counted − (snapshot + movements since opening). Sales and receipts may therefore continue during the count.
  • Uncounted lines are excluded from validation unless « Considérer les non comptés comme 0 » is ticked.
  • A validated session is read-only.

Real-time. None.

States. « Aucun inventaire en cours ». Concurrent counting on two tablets is last-write-wins per line, with the counter's name shown.

Acceptance criteria.

  • A shipment posted during a session does not create a false difference at validation.
  • Validation creates exactly one INVENTORY movement per line with a non-zero difference.
  • Blind mode never shows the expected quantity.

BO-37 — Warehouses

FieldValue
TemplateList (with drawer)
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-05
Route/stock/warehouses
RequirementsSTK-01
Permissions requiredstock.administer

Purpose. Declare the warehouses when HOCO manages stock per warehouse (cahier §4.3 « Stock global ou par entrepôt »).

Who uses it. Stock manager, super admin.

Entry points. Stock › Entrepôts.

Layout.

  • Table: Nom, Code, Wilaya / commune, Par défaut (expédition), Actif, Variantes en stock, Valeur (needs pricing.view).
  • Drawer:
    • Nom and Code: required, unique.
    • Adresse, Wilaya, Commune.
    • Entrepôt par défaut (one only).
    • Actif.

Data. admin.warehouses.crud.

Actions. Create and edit (audited). Deactivating is refused while physical stock is above 0: « Videz l'entrepôt avant de le désactiver ».

Rules.

  • Exactly one default warehouse; it reserves and ships when the order flow does not pick another.
  • With a single warehouse, the stock pages hide the warehouse dimension.

Real-time. None.

States. One seeded warehouse (« Dépôt principal »).

Acceptance criteria.

  • Setting a second default warehouse removes the flag from the first.
  • A warehouse with stock cannot be deactivated.

BO-71 — Product attributes (filters) per category

FieldValue
TemplateList (category tree + attribute list with drawer)
ReleaseV1
Design dayDay 17 — DES-17
Build taskDSH-04
Route/catalog/attributes (?category=)
RequirementsCAT-13 · CAT-12
Permissions requiredcatalog.view · catalog.create · catalog.update · catalog.delete

Purpose. Define, per category, the filterable attributes resellers use to narrow the catalog: power in W, connector USB-C / Lightning / Micro-USB, cable length, colour, battery capacity in mAh, Bluetooth version (§1b.7). Each definition has a type, unit, allowed values and filter widget, and is valued on every variant in BO-28.

Who uses it. Stock manager, super admin.

Entry points. Catalogue › Attributs et filtres (badge = variants with missing required values), « Attributs » on a BO-30 node, and the BO-28 Attributs section (« Gérer les attributs de la catégorie »).

Layout.

  • Left: the category tree, with the attribute count per node.
  • Right: the attributes of the selected category. Those inherited from the parent category are shown greyed, with « Hérité de Câbles ». Columns: Libellé, Clé, Type, Unité, Valeurs, Obligatoire, Filtre (widget), Ordre, Variantes renseignées (« 182 / 190 »).
  • Drawer:
FieldTypeValidationDefaultHelp text
LibelléText (FR; AR and EN prepared)Required, 40 characters max—Shown to resellers
CléSlug a–z 0–9 _Required, unique within the category branch; immutable once usedGenerated from the labelUsed in imports (attr.<clé>)
TypeSelect: « Nombre », « Liste à choix unique », « Liste à choix multiples », « Oui / Non »Required—Assumption — the contract will carry these as NUMBER, ENUM, MULTI_ENUM, BOOLEAN
UnitéTextFor numbers, 8 characters max—« W », « m », « mAh »
PlageMin, max, stepFor numbers; min below max—Validates variant values
Valeurs autoriséesOrdered list: label and code, drag to reorderFor lists, 1 to 50 values; unique codes—« USB-C », « Lightning », « Micro-USB »
Widget de filtreSelect: checkboxes, chips, range slider, toggleCompatible with the typeInferred from the typeHow the filter looks in the apps and web
Obligatoire sur les variantesToggle—OffRequired values block publication (BO-28)
Afficher dans les filtresToggle—On—
Afficher sur la fiche produitToggle—OnAdds the value to the technical sheet

Examples to seed. Assumption — to validate with HOCO.

CategoryAttributeTypeUnit / values
ChargeursPuissanceNumberW, range slider
ChargeursPortsMultiple choiceUSB-A, USB-C
ChargeursCharge rapideYes / No—
CâblesConnecteurSingle choiceUSB-C, Lightning, Micro-USB
CâblesLongueurNumberm (0,25–3)
Câbles, ÉcouteursCouleurSingle choiceNoir, Blanc, …
PowerbanksCapacitéNumbermAh
Écouteurs, AudioVersion BluetoothSingle choice5.0, 5.1, 5.3

Data. admin.attributes.crud (AttributeDefinition), admin.categories.crud (tree).

Actions.

  • Create, edit, reorder: audited.
  • Removing an allowed value used by variants asks: « 37 variantes utilisent Micro-USB. Remplacer par : [select] ou laisser vide ».
  • Deleting a definition with values asks: « Supprimer l'attribut Longueur ? Les valeurs de 190 variantes seront supprimées et le filtre disparaîtra des applications. »
  • Making an attribute required shows how many active variants miss it.

Rules.

  • Inheritance: definitions apply to their category and its sub-categories.
  • Keys are immutable once a variant has a value, so imports and facets stay stable.
  • Facets: changes are reflected in catalog.facets and the reseller filters on the next request.
  • Price-range facets: these are computed from the caller's own customer-type price, never shown to guests (CAT-12).

Real-time. None.

States.

  • A category without attributes: « Aucun attribut — les revendeurs ne pourront filtrer que par marque, prix et disponibilité ».
  • The completeness bar per attribute turns amber below 100 % for required ones.

Acceptance criteria.

  • Adding Connecteur (Lightning, USB-C, Micro-USB) to Câbles adds the column to every cable product in BO-28 and a filter with counts in the reseller catalog.
  • A required attribute left empty on an active variant blocks that product's publication and shows it in the sidebar badge.
  • A used key cannot be changed. Replacing a removed value moves all variants to the chosen value.

On this page