HOCO B2B
DesignScreen specificationsBack-office

Access and dashboards

Page specs BO-01 to BO-08: sign-in, password reset, the app shell, and the general, sales, field, stock and finance dashboards.

These pages open the back-office and give every role its daily picture. The shell (BO-03) carries the global elements described in the overview. The general dashboard (BO-04) fixes the KPI definitions that the business dashboards and the reports reuse.

IDScreenSectionTemplateReleaseDesignBuild
BO-01Sign inAccessbespokeV1Day 15DSH-01
BO-02Forgot / reset passwordAccesstemplateV1Day 15DSH-01
BO-03App shell: navigation by role, global search, notificationsAccessbespokeV1Day 15DSH-01
IDScreenSectionTemplateReleaseDesignBuild
BO-04General dashboardDashboardsbespokeV1Day 15DSH-02
BO-05Sales dashboardDashboardstemplateV1Day 15DSH-02
BO-06Field dashboard & live mapDashboardsbespokeV1Day 15DSH-08
BO-07Stock dashboardDashboardstemplateV1Day 15DSH-05
BO-08Finance dashboardDashboardstemplateV1Day 15DSH-07

BO-01 — Sign in

FieldValue
TemplateBespoke
ReleaseV1
Design dayDay 15 — DES-15
Build taskDSH-01
Route/sign-in (?next= an internal path)
RequirementsAUTH-01 · AUTH-08 · SEC-02 · AUD-01
Permissions requiredNone (public page). After sign-in, at least one back-office permission.

Purpose. Open a back-office session with an email or phone number and a password (cahier §3, §10). Refuse everyone who is not staff with an explicit message.

Who uses it. All six staff roles.

Entry points.

  • Any back-office URL without a valid session (redirect with next).
  • « Se déconnecter ».
  • A 401 on any call.
  • The end of the password reset (BO-02).
  • The first-sign-in link sent with a temporary password when an administrator creates the account (BO-61).

Layout.

  • Desktop: split screen.
    • The left half is the brand panel: HOCO logo, the three brand marks HOCO · ACEFAST · BOROFONE, and the line « Back-office B2B ».
    • The right half holds a 400 px form card: title « Connexion », the two fields, the primary button « Se connecter », and the link « Mot de passe oublié ? ».
  • Tablet portrait: one column, with the brand panel reduced to a header band.
  • Footer: build version. Outside production, a ribbon names the environment (« Staging », « Recette ») so no one confuses it with production.
FieldTypeValidationDefaultHelp text
IdentifiantText, autocomplete="username"Required. An email, or a phone in national or E.164 format (normalised before sending).Empty, or the value passed by BO-02« Email ou numéro de téléphone »
Mot de passePassword with a show/hide toggle, autocomplete="current-password"Required——

First sign-in step. When me.get reports that the password is temporary (Assumption: a mustChangePassword flag), a second card replaces the form: « Choisissez votre mot de passe ». It has Nouveau mot de passe and Confirmation, with the live policy checklist from BO-02. No other page is reachable until the password is changed.

Data.

  • auth.login: sends the identifier and password. The API sets the httpOnly session cookie on .hoco.dz.
  • me.get: userType, roles, permissions, status, temporary-password flag.
  • auth.password.change: first sign-in.
  • auth.logout: rejected user types.

Actions.

ActionEndpointBehaviourAudit
« Se connecter »auth.login, then me.getThe button shows a spinner and the fields are disabled. On success, go to next if it is an internal back-office path, else to the home page (/ with dashboard.view, otherwise the first visible sidebar item).LOGIN or LOGIN_FAILED, with IP, user agent and app DASHBOARD
« Mot de passe oublié ? »—Opens BO-02 with the identifier prefilled—
« Enregistrer mon mot de passe » (first sign-in)auth.password.changeThen continues to nextUPDATE on the user

Rules.

  • Rate limits (AUTH-07): 5 attempts per minute per identifier and IP, and a 15-minute lockout after 5 consecutive failures. While locked, the button is disabled and a countdown runs from Retry-After: « Trop de tentatives. Réessayez dans 14:32. »
  • No account enumeration: 401 INVALID_CREDENTIALS always reads « Identifiant ou mot de passe incorrect. »
  • Blocked accounts:
    • 403 ACCOUNT_SUSPENDED reads « Votre compte est suspendu. Contactez votre administrateur. »
    • A disabled account reads « Ce compte est désactivé. »
  • Non-staff users: when me.get returns a userType other than STAFF, the page calls auth.logout and shows « Ce compte n'a pas accès au back-office. Utilisez l'application HOCO Délégué ou HOCO Revendeur. »
  • Open-redirect guard: next must be a relative path inside the back-office. Anything else is dropped.
  • Session: 7 days, sliding, per AUTHORING (decision pending).
  • Password managers: correct autocomplete attributes, and paste is never blocked.

Real-time. None.

States.

  • Submitting: the button spins and the fields are disabled.
  • Wrong credentials: an inline error above the button; the password is cleared and gets focus.
  • Locked: countdown and disabled button.
  • Suspended or disabled: an error panel without a retry button.
  • Errors: 503 MAINTENANCE shows the full-page maintenance message. A network failure reads « Connexion impossible — vérifiez votre réseau. »
  • Already signed in: the page redirects to the home page.

Acceptance criteria.

  • A staff user with valid credentials lands on next (or the home page), and the sidebar shows exactly the items their permissions allow.
  • After 5 wrong passwords, the next attempt is refused for 15 minutes and the countdown is shown.
  • Delegate or reseller credentials never leave a back-office session cookie, and the message names their app.
  • A suspended account sees the suspension message and no session is created.
  • A user created with a temporary password cannot reach any page before setting a new password.
  • LOGIN and LOGIN_FAILED entries appear in the activity log with IP and app DASHBOARD.

BO-02 — Forgot / reset password

FieldValue
TemplateForm (three-step, on the public sign-in layout)
ReleaseV1
Design dayDay 15 — DES-15
Build taskDSH-01
Route/forgot-password
RequirementsAUTH-07 · SEC-02
Permissions requiredNone (public page)

Purpose. Let staff reset a forgotten password with a one-time code, without revealing whether an account exists.

Who uses it. All staff roles.

Entry points. « Mot de passe oublié ? » on BO-01, and the link in the « mot de passe réinitialisé » message an administrator triggers from BO-61.

Layout. Three steps in the same card, with a step indicator (1 Identifiant · 2 Code · 3 Nouveau mot de passe) and « Retour à la connexion » at the bottom.

StepFieldTypeValidationHelp text
1IdentifiantTextRequired; an email or a phone« Le code est envoyé par SMS au numéro du compte. »
2CodeSix single-digit boxes, paste-aware, auto-advance6 digits« Code valable 5 minutes »
3Nouveau mot de passePassword with a show toggle and a live checklistAt least 10 characters, at least one letter and one digit, different from the identifier (Assumption — AUTH-07 sets the final policy)Checklist items turn green as they are met
3ConfirmationPasswordMust equal the new password—

Data.

Actions.

ActionEndpointBehaviour
« Recevoir un code »auth.otp.sendAlways shows « Si un compte correspond, un code à 6 chiffres vient d'être envoyé. », then step 2
« Vérifier »auth.otp.verifyWrong code: « Code incorrect — 3 essais restants. » Expired: « Code expiré. Demandez un nouveau code. »
« Renvoyer le code »auth.otp.sendDisabled for 60 s with a countdown. At most 5 sends per hour.
« Enregistrer le mot de passe »auth.password.resetThen « Mot de passe modifié. Vos autres sessions ont été fermées. » and BO-01 with the identifier prefilled

Rules.

  • Code: 6 digits, valid 5 minutes, 5 attempts per code. Codes are sent at most once every 60 s and 5 times per hour per phone (AUTH-07).
  • After a reset, every session of the user is revoked (SEC-02). A security message (« Votre mot de passe HOCO a été modifié ») goes out on the account's channel. It belongs to the SECURITY category, which cannot be disabled.
  • Staff without a phone: the code goes by email. Assumption — auth.otp.send must support email delivery for staff accounts (NOT-03).
  • The reset is recorded in the activity log (UPDATE on the user, without the password).

Real-time. None.

States.

  • Sending and verifying show spinners.
  • 429 OTP_RESEND_TOO_SOON shows the remaining wait. 429 TOO_MANY_ATTEMPTS reads « Trop de tentatives. Demandez un nouveau code. »
  • After a step-3 failure, step 3 is shown again with the server message.

Acceptance criteria.

  • An unknown identifier and a known one produce the same screen and the same wording.
  • A code older than 5 minutes is refused. The resend button stays disabled for 60 s after each send.
  • After a reset, a session open in another browser is signed out at its next request.
  • The new password signs in on BO-01. The old one returns INVALID_CREDENTIALS.

BO-03 — App shell: navigation by role, global search, notifications

FieldValue
TemplateBespoke
ReleaseV1
Design dayDay 15 — DES-15
Build taskDSH-01
RouteWraps every authenticated route
RequirementsACL-05 · NOT-01 · SYN-07 · NFR-05
Permissions requiredA session. Each sidebar item needs its own permission (see sidebar). Search results are filtered by permission.

Purpose. Frame every page: navigation computed from the user's permissions, global search, notifications, live events, and the user menu.

Who uses it. Every staff user, on every page.

Entry points. It loads after sign-in and wraps all authenticated routes.

Layout.

  • Sidebar (256 px; 72 px icon rail when collapsed):
    • HOCO logo with the environment ribbon outside production.
    • Groups and items as in the sidebar table, with badges.
    • The active item is highlighted. « Réduire le menu » at the bottom (shortcut [).
  • Top bar (56 px):
    • Breadcrumb.
    • Search field « Rechercher… » with the Ctrl K hint.
    • Live-status dot (green « En direct », amber « Reconnexion… », grey « Hors ligne »).
    • Bell with the unread badge.
    • Avatar and user menu.
  • Search palette: a centred modal of 640 px. The input sits on top and results are grouped below, with keyboard hints.
  • Notifications drawer: 400 px on the right (see overview).
  • Re-authentication dialog: « Confirmez votre mot de passe pour continuer », shown before sensitive actions.
  • Global banners under the top bar: offline, maintenance announced, real-time interrupted.

Data.

Actions.

ActionEndpointBehaviour
Open search (Ctrl K or click)admin.searchDebounced 250 ms, at least 2 characters. Enter on an exact business number opens the record.
Open a notificationnotifications.markRead (optimistic)Marks it read, navigates to the deep link and closes the drawer
« Tout marquer comme lu »notifications.markAllRead (optimistic)The badge drops to 0 and is rolled back on error
Collapse or expand the sidebar—The choice is stored in the browser
« Se déconnecter »auth.logoutCloses the event stream, clears the query cache, goes to BO-01
Confirm the password (re-authentication)auth.login with the current identifier (Assumption — a dedicated re-auth operation may replace it)On success the pending action runs. On failure the dialog shows the error.

Rules.

  • Navigation follows permissions (ACL-05). Item requirements live in one configuration file (navigation.ts), each item listing the permission keys it needs. A route guard applies the same keys, so a typed URL without the permission shows « Accès refusé ». The server stays the authority.
  • Permission refresh: me.get is re-fetched on window focus, every 5 minutes and after any 403, so permission changes made in BO-62 reach the shell without a new sign-in.
  • Counters: never computed for modules the user cannot view.
  • Keyboard shortcuts:
    • Ctrl K: search.
    • Ctrl/Cmd+S: save the current form.
    • Esc: close a drawer, dialog or palette.
    • [: collapse the sidebar.
    • ?: shortcut overlay.

Real-time. The shell owns the event stream and the toasts. Each event invalidates the matching queries and badges, as listed in live events.

States.

  • Booting: logo, then a skeleton sidebar.
  • me.get failing: full-page error with « Réessayer ».
  • Connectivity: offline banner; real-time indicator amber after 30 s without the stream.
  • 503 MAINTENANCE: maintenance page.
  • Expired session: redirect to BO-01 with next.
  • No permission at all (staff account without roles): page « Aucun accès n'est attribué à votre compte. Contactez votre administrateur. »

Acceptance criteria.

  • A user holding only orders.view sees only the Commandes group (Commandes, Retours et SAV) and lands on /orders. Commandes à confirmer appears only with orders.validate.
  • A new order placed in the reseller app increments the Commandes à confirmer badge within 5 s, with no reload. Confirming it decrements the badge in every open tab.
  • After a super admin removes crm.view from a role, a signed-in holder loses the CRM group within 5 minutes or at next focus, without signing out.
  • Ctrl K then CMD-2026-000158 + Enter opens that order's detail.
  • A finance manager's search never returns orders or resellers they cannot view, and the server response contains no such group.
  • Badges match the corresponding list totals.
  • With the API stream stopped, the live dot turns amber within 30 s and recovers automatically when it returns.

BO-04 — General dashboard

FieldValue
TemplateBespoke (Dashboard skeleton)
ReleaseV1
Design dayDay 15 — DES-15
Build taskDSH-02
Route/
RequirementsANA-01 · ANA-02 · ANA-04 · ORD-14 · ACL-04
Permissions requireddashboard.view to open the page. Each card also needs its module's view permission (table below). settings.administer to customise the layout of a role.

Purpose. Give an immediate picture of activity, alerts and performance (cahier §4.1): revenue for the day, month and year; orders by status and average basket; active, new and inactive resellers; delegates on tour, visits done and conversion; best sellers, stock-outs and stock alerts; expected payments, credit used and unpaid amounts. All of it is filterable by date, wilaya, commune, delegate, brand and category. Since no order is confirmed automatically (ORD-14), the page also leads with the live count of orders waiting for confirmation.

Who uses it. Every role on arrival. Each role gets its own card layout. Direction uses it read-only as its main screen.

Entry points. The home page after sign-in, the logo, and Pilotage › Tableau de bord.

Layout

  1. Filter bar (sticky):
    • Période, Wilaya, Commune (enabled once a wilaya is chosen), Délégué, Marque, Catégorie.
    • « Réinitialiser ».
    • « Mis à jour à 09:42 » and a refresh button.
    • « Personnaliser » (only with settings.administer).
  2. Chiffre d'affaires row:
    • three KPI cards: CA du jour, CA du mois, CA de l'année;
    • the chart Évolution du CA (two columns wide): daily buckets, weekly beyond 92 days, monthly beyond 366 days. The comparison period is a dashed line.
  3. Commandes row:
    • Commandes à confirmer, the first card of the row, with an accent border while it is above 0. A pulse marks each live update.
    • Commandes par statut: wide card, horizontal stacked bar with one clickable segment per status.
    • Panier moyen.
  4. Revendeurs row: Revendeurs actifs, Nouveaux revendeurs, Revendeurs inactifs.
  5. Terrain row: Délégués en tournée, Visites réalisées, Taux de conversion (with a mini-funnel).
  6. Produits et stock row: Produits les plus vendus (list card, two columns wide), Ruptures, Alertes de stock.
  7. Finance row: Paiements attendus, Crédit utilisé, Impayés.
  8. « À traiter » panel: right column on large screens, last row otherwise. Each line is a count with a link, shown only with its permission:
    • orders to confirm waiting more than 2 h, and orders on hold (PENDING) for more than 24 h;
    • accounts and points of sale to validate;
    • return requests to decide and after-sales claims without an assignee;
    • payment declarations to verify;
    • unassigned tickets;
    • tours not started one hour after their planned start.

Filters

FilterControlURL parameterNotes
PériodePresets + date-range pickerperiod = today (default), yesterday, last7, last30, month, lastMonth, year, custom; with custom, from and to (YYYY-MM-DD)Custom range: at most 366 days, from ≤ to, never in the future. Days are business days in Africa/Algiers.
WilayaSearchable select (code + name)wilaya (code, e.g. 31)From reference.wilayas
CommuneSearchable selectcommune (id)From reference.communes. Cleared when the wilaya changes.
DéléguéSearchable select with avatarsdelegate (id)Active and suspended delegates
MarqueSelect (HOCO, ACEFAST, BOROFONE)brand (id)—
CatégorieTree selectcategory (id)Includes sub-categories

Common definitions used by the formulas:

  • Counted order: an order whose validatedAt is set (a person confirmed it in the back-office; nothing is confirmed automatically) and whose current status is none of CANCELLED, REFUSED, RETURNED. A later cancellation or return therefore removes the order from past periods too: figures are computed at query time.
  • Net HT: subtotalHt − discountTotal of the order, so delivery fees and VAT are excluded. With a brand or category filter, only the net HT of the matching lines is summed.
  • Anchor date: the end date of the selected period (today by default).
  • Activity window: the setting crm.activityWindowDays, default 60 days. Assumption — this is the same window that drives the automatic ACTIVE reseller status.
  • Geography: for orders, the order's delivery-address snapshot; for resellers, their main address; for tours and visits, the point of sale's commune.
  • Delegate: for orders, the attributed delegate (the source delegate of an assisted order, else the reseller's assigned delegate at checkout; see contract notes); for resellers, the assigned delegate; for tours and visits, the tour's delegate.
  • Amounts are in DA (integer centimes in the API).
  • Decision to confirm: revenue is shown HT and on the validation basis, not the delivery or invoice basis (open decisions).

KPI cards

CardKeyFormulaPeriod behaviourFilters appliedNeedsClick opens
CA du jourrevenue.dayΣ net HT of counted orders with validatedAt on the anchor dateAnchored on the anchor date. Δ vs the same weekday of the previous week; when the anchor is today, up to the same time of day.All sixorders.viewBO-38, validated on that day
CA du moisrevenue.monthSame sum, validatedAt from the 1st of the anchor's month to the anchor dateΔ vs the same day span of the previous month (capped at that month's length)All sixorders.viewBO-38, same range
CA de l'annéerevenue.yearSame sum, from 1 January to the anchor dateΔ vs the same span of the previous yearAll sixorders.viewBO-59 CA par période, by month
Commandes à confirmerorders.toConfirmOrders currently SUBMITTED or PENDING (the BO-41 queue). Second lines: how many are on hold (PENDING); the age of the oldest (« la plus ancienne : 3 h 10 »); the median confirmation delay (validatedAt − submittedAt) of orders confirmed today. Red tone once the oldest waits more than 2 h (setting orders.confirmationAlertMinutes, Assumption 120).Live snapshot; ignores the periodGeography, delegateorders.viewBO-41 with orders.validate, otherwise BO-38 ?status=SUBMITTED&status=PENDING
Commandes par statutorders.byStatusOpen statuses (SUBMITTED, PENDING, VALIDATED, PREPARING, READY, SHIPPED): count of orders currently in the status. Closing statuses (DELIVERED, CANCELLED, REFUSED, RETURNED): count of orders that entered the status during the period (OrderStatusChange). DRAFT is never counted.Period applies to closing statuses only; open statuses are a live snapshotGeography, delegate; brand and category = orders with at least one matching lineorders.viewBO-38 ?status=<value>
Panier moyenorders.averageBasketΣ net HT of counted orders validated in the period ÷ number of those orders. « — » when there are none.Period; Δ vs the previous period of equal lengthAll sixorders.viewBO-59 CA par période
Revendeurs actifsresellers.activeDistinct resellers with at least one counted order validated in the activity window ending on the anchor dateAnchored; Δ vs the same count at the end of the previous periodGeography (main address), delegate (assigned); brand and category = orders containing themcrm.viewBO-09 ?activity=active
Nouveaux revendeursresellers.newResellers whose validation turned APPROVED during the period. Second line: how many of them already reached FIRST_ORDER.PeriodGeography, delegatecrm.viewBO-09 ?approvedFrom=…&approvedTo=…
Revendeurs inactifsresellers.inactiveResellers whose account was approved before the activity window started, whose status is neither SUSPENDED nor NOT_INTERESTED, with no counted order in the window ending on the anchor dateAnchoredGeography, delegatecrm.viewBO-09 ?activity=inactive
Délégués en tournéefield.delegatesOnTourDelegates with a tour IN_PROGRESS or SUSPENDED now ÷ delegates with a tour dated today that is not CANCELLED. Second line: how many are suspended.Live snapshot; ignores the periodWilaya and commune (tours with at least one stop there), delegatefield.viewBO-06
Visites réaliséesfield.visitsDoneVisits DONE with a check-in in the period ÷ visits planned in the period (planned date in the period, status not CANCELLED), shown as « 46 / 58 · 79 % »PeriodGeography, delegatefield.viewBO-24, same period
Taux de conversionfield.conversionCohort of prospects created in the period with source DELEGATE_APP or BACK_OFFICE (N). Of those, reached ACCOUNT_CREATED so far (A) and reached FIRST_ORDER so far (F). Shows F ÷ N and the mini-funnel N → A → F (cahier §1.4). The tooltip warns that recent cohorts are still maturing.Period = cohort creation datesGeography, delegate (creating or assigned)field.view + crm.viewBO-59 Conversion terrain
Produits les plus venduscatalog.bestSellersTop 10 products by quantity in counted orders validated in the period, with a « Quantité / CA HT » toggle. Each row: rank, product, brand, quantity, net HT, share of the period's revenue.PeriodAll sixorders.viewThe product's BO-28
Rupturesstock.outOfStockActive variants of products ACTIVE or OUT_OF_STOCK whose available stock (physical − reserved; global, or summed over warehouses) is ≤ 0Live snapshotBrand, category. Other filters show « non applicable ».stock.viewBO-33 ?alert=OUT
Alertes de stockstock.lowStockActive variants with 0 < available ≤ their alert thresholdLive snapshotBrand, categorystock.viewBO-33 ?alert=LOW
Paiements attendusfinance.expectedPaymentsΣ remaining balance (total TTC − confirmed allocations) of invoices UNPAID or PARTIALLY_PAID with a due date on or after the anchor date. Second lines: the part due within 7 days; declarations PENDING_VERIFICATION (count and amount).Snapshot on the anchor dateGeography, delegate (the invoice's reseller)finance.viewBO-44 ?status=UNPAID&status=PARTIALLY_PAID
Crédit utiliséfinance.creditUsedΣ used credit ÷ Σ credit limits over credit accounts with a limit above 0, shown as « 4,2 M DA / 9,0 M DA · 47 % ». Used credit = the exposure computed by billing: unpaid balances of invoices paid on credit, plus orders paid on credit that are validated but not yet invoiced. Second line: accounts at 90 % or more, and blocked accounts.SnapshotGeography, delegatefinance.viewBO-46 ?usage=90
Impayésfinance.overdueΣ remaining balance of OVERDUE invoices. Second line: number of resellers, and the oldest overdue age in days.SnapshotGeography, delegatefinance.viewBO-44 ?status=OVERDUE

Default layout per role (editable, ANA-02). A role can only be given cards whose permission it holds.

RoleDefault cards
Super admin, DirectionAll 18, plus Évolution du CA and « À traiter »
Commercial adminCommandes à confirmer, revenue ×3, Évolution du CA, Commandes par statut, Panier moyen, resellers ×3, field ×3, Produits les plus vendus, « À traiter »
Stock managerCommandes à confirmer (to anticipate picking), Commandes par statut, Produits les plus vendus, Ruptures, Alertes de stock
Finance managerRevenue ×3, Panier moyen, Paiements attendus, Crédit utilisé, Impayés, « À traiter »
Delivery managerCommandes à confirmer, Commandes par statut, Ruptures, « À traiter »

Customisation mode.

  • « Personnaliser » opens an edit mode with a Rôle selector.
  • In edit mode, cards can be dragged on the grid, hidden (✕) or added from a palette of the cards the role is allowed. « Rétablir la disposition par défaut » resets the role.
  • « Enregistrer » saves the layout for every user of that role.

Data.

Actions.

ActionEndpointConfirmationOptimisticAudit
Change a filteradmin.dashboard.kpis, admin.dashboard.charts—Previous values stay dimmed until the new ones arrive—
Click a card or a status segment————
« Personnaliser » → « Enregistrer »admin.dashboard.layout.update« Appliquer cette disposition à tous les utilisateurs du rôle Responsable stock ? »NoUPDATE on DashboardLayout
« Rétablir la disposition par défaut »admin.dashboard.layout.update« Rétablir la disposition par défaut pour ce rôle ? »NoUPDATE

Rules.

  • Permission-aware (ANA-02, cahier §4.1):
    • The server returns only the cards the user may see. The client never hides a value it received and never computes a KPI itself.
    • Amounts that depend on purchase prices (margins) are not on this page.
  • One set of definitions: the same formulas back BO-05, BO-08 and the reports in BO-59. A report run with the same filters returns the same total as the card.
  • Updates after business events (cahier §4.1): see Real-time. The footer timestamp asOf comes from the server.
  • Filters that do not apply to a card (brand on finance cards, geography on stock cards) are listed in notApplicableFilters. The card then shows the muted hint « Filtre Marque non applicable ».

Real-time.

  • order.created, order.status_changed → Commandes à confirmer updates immediately (no debounce: +1 on a new order, −1 when an order leaves SUBMITTED / PENDING, then a background refetch to correct drift). Revenue, orders, basket, best sellers and reseller cards refetch, debounced 10 s.
  • tour.started, tour.closed, visit.checked_in, visit.completed → field cards.
  • stock.alert → stock cards.
  • account.pending → the « À traiter » panel.
  • Finance cards have no event: they refresh every 5 minutes and on focus.

States.

  • Loading: one skeleton per card. The chart skeleton has axes.
  • Empty period: « Aucune commande validée sur la période » inside the revenue cards, with values « 0,00 DA » and Δ « — ».
  • Card error: that card alone shows « Indicateur indisponible » and « Réessayer ».
  • No permitted card (dashboard.view without any module permission): « Aucun indicateur n'est disponible pour votre profil. »
  • Custom range over 366 days: the picker refuses it with « Période limitée à 366 jours ».

Acceptance criteria.

  • With wilaya 31 and brand ACEFAST, CA du mois equals the total of the CA par période report in BO-59 for the same filters.
  • Cancelling a validated order removes its amount from the three revenue cards within 15 s while the stream is connected.
  • A finance manager's admin.dashboard.kpis response contains no field or reseller cards. A commercial admin's response contains no finance cards. Both are checked on the API response, not only in the UI.
  • Mois précédent anchors CA du jour / du mois / de l'année on the last day of the previous month.
  • Each card's ⓘ tooltip shows the definition from the table above, in French.
  • After a super admin hides Ruptures for the stock-manager role, a stock manager no longer sees it after a reload.
  • Délégués en tournée changes within 5 s of a delegate starting a tour.
  • An order submitted from the reseller app raises Commandes à confirmer by 1 within 5 s. Confirming it in BO-41 lowers it by 1, and the order then counts in CA du jour under its validation time.
  • With the oldest order to confirm waiting over 2 h, the card turns red and « À traiter » lists it.

BO-05 — Sales dashboard

FieldValue
TemplateDashboard
ReleaseV1
Design dayDay 15 — DES-15
Build taskDSH-02
Route/dashboards/sales
RequirementsANA-01 · ANA-03 · ANA-04
Permissions requireddashboard.view + orders.view. The delegate ranking also needs field.view.

Purpose. Break revenue down by period, zone, delegate, reseller, brand and product (cahier §4 « Indicateurs et rapports »), and track how much ordering has moved to the apps.

Who uses it. Commercial admin, direction, super admin. Finance reads it for context.

Entry points. Pilotage › Ventes, and « Voir le détail des ventes » from the revenue chart on BO-04.

Layout.

  • Filter bar: the BO-04 filters plus Canal (channel, multi-select of OrderChannel) and Granularité (granularity = day, week or month; default chosen from the period length).
  • KPI cards:
    • CA HT de la période.
    • Commandes (counted orders).
    • Panier moyen.
    • Part des commandes digitales: orders from RESELLER_APP + RESELLER_WEB ÷ all counted orders, the cahier §1.4 digitalisation indicator.
    • Premières commandes: orders flagged first order and validated in the period (ORD-09).
    • Taux d'annulation: orders that entered CANCELLED or REFUSED in the period ÷ orders submitted in the period.
  • Charts:
    • CA par période (line, with the previous period dashed).
    • CA par marque (donut over HOCO, ACEFAST, BOROFONE).
    • CA par catégorie (horizontal bars).
    • CA par wilaya (bars for the top 15, « Autres » grouped).
    • Commandes par canal (stacked bars per bucket).
  • Tables:
    • Classement des délégués: delegate, CA HT, orders, first orders, Δ vs the previous period. Needs field.view.
    • Top revendeurs: reseller, CA HT, orders, last order.
    • Top produits: product, brand, quantity, CA HT.

Data.

  • admin.dashboard.kpis (with dashboard=sales, Assumption).
  • admin.dashboard.charts with revenue-trend, revenue-by-brand, revenue-by-category, revenue-by-wilaya, orders-by-channel, top-delegates, top-resellers, top-products.

Actions.

  • Click any bar, slice or row to open the matching BO-59 report or list with the same filters (for example a wilaya bar → CA par zone with wilaya set).
  • « Exporter » a chart's data as XLSX or CSV through admin.exports.create (reports.export).

Rules. Definitions of counted orders, net HT, geography and delegate are exactly those of BO-04. A brand or category filter restricts lines, not orders, so the brand donut always sums to the filtered revenue.

Real-time. order.created and order.status_changed refresh the cards and charts (debounced 10 s).

States.

  • Each chart has its own skeleton and error.
  • « Aucune vente sur la période ».
  • Without field.view, the delegate ranking is not rendered.

Acceptance criteria.

  • The donut slices sum to CA HT de la période to within 1 DA (rounding).
  • The digital share for a day equals the BO-38 count with channel=RESELLER_APP&channel=RESELLER_WEB divided by all counted orders of that day.
  • A click on the ACEFAST slice opens CA par marque in BO-59 with brand set to ACEFAST and the same period.
  • Changing the granularity to week regroups the trend without changing the period total.

BO-06 — Field dashboard & live map

FieldValue
TemplateBespoke
ReleaseV1
Design dayDay 15 — DES-15
Build taskDSH-08
Route/dashboards/field (?mode=screen for the wall display)
RequirementsFLD-13 · SYN-07 · FLD-15 · INT-01
Permissions requireddashboard.view + field.view

Purpose. Follow today's field activity live (cahier §4.5 « Suivi du démarrage, des visites, des positions et de la clôture »): who is on tour, where, how far along, and what needs attention.

Who uses it. Commercial admin (field supervision), direction, super admin.

Entry points.

  • Pilotage › Terrain.
  • The Délégués en tournée card on BO-04.
  • The live badge in the sidebar.

Layout.

  • KPI strip (today):
    • Délégués en tournée (4 / 6).
    • Visites réalisées (46 / 58).
    • Check-ins hors tolérance: arrivals beyond the GPS tolerance, 150 m by default.
    • Nouveaux prospects, Comptes créés and Premières commandes, all for today.
  • Map (two thirds of the width, full height):
    • Delegate markers: avatar initials, coloured by tour status. IN_PROGRESS uses the progress tone, SUSPENDED the warning tone, and a position older than 15 min turns grey with the label « il y a 17 min ».
    • Stops: today's stops as small numbered dots coloured by VisitStatus, with derived « En retard » in the danger tone.
    • Popover on a delegate marker: name, TRN number, progress « 7 / 12 », current or next stop, time and accuracy of the last position, km today. Actions: « Ouvrir la tournée » (BO-23) and « Appeler ».
  • Side panel (one third):
    • Tournées du jour, in-progress first. Each row: delegate, TRN, status pill, a done/planned progress bar, late visits, last-position age, km.
    • Filter chips: Zone, Statut.
    • Alertes feed, newest first:
      • visit not done (with its reason);
      • check-in out of tolerance (with the distance);
      • position stale for more than 15 min while IN_PROGRESS;
      • tour not started 60 min after its planned start;
      • visit late, meaning still planned after its time slot. Assumption — the 60-min delay is a setting to confirm.
  • « Mode écran » (?mode=screen, large screens): full screen without the sidebar, larger markers, the map fitted to all delegates, the alerts feed only.

Data.

Actions.

ActionEndpointBehaviour
Click a delegate (map or list)—Opens the popover, then « Ouvrir la tournée » goes to BO-23
« Appeler »—tel: link to the delegate's phone
Filter by zone or status—Updates zone and status in the URL and refits the map
« Mode écran »—Toggles ?mode=screen

Rules.

  • Positions exist only while a tour is IN_PROGRESS (sampling every 5 min or 200 m; FLD-15).
    • After a suspension, the last known position stays visible in grey with its time.
    • After the closing, the marker disappears.
    • Nothing older than the retention period (90 days) is ever shown.
  • Tolerance alerts use the settings field.gpsToleranceMeters (150 m) and field.maxAccuracyMeters (100 m). A check-in whose accuracy exceeds 100 m is flagged « Précision GPS insuffisante » rather than out of tolerance.
  • Map provider: Google Maps or Mapbox (INT-01, decision pending).

Real-time.

  • delegate.position moves the marker (animated in at most 1 s) and resets its age.
  • tour.started adds the delegate.
  • tour.closed removes the marker and marks the row Terminée.
  • visit.checked_in turns the stop Arrivé and may raise a tolerance alert.
  • visit.completed turns the stop Visité or Non réalisée and updates the counters.

States.

  • No tour today: « Aucune tournée aujourd'hui », with a link to the planner.
  • Delegate without a position yet: listed with « En attente de la première position ».
  • Map provider failure: the list and alerts stay usable, with « Carte indisponible ».
  • Stream lost: banner, and admin.live.delegates polled every 30 s.

Acceptance criteria.

  • A delegate who starts a tour appears on the map within 5 s, without a reload.
  • Each delegate.position event moves the marker. With no ping for 15 min, the marker turns grey and shows its age.
  • A check-in 400 m from the point of sale adds an alert « Check-in à 400 m du point de vente ».
  • The counters match BO-24 filtered on today.
  • After the tour is closed, the delegate's position is no longer displayed.

BO-07 — Stock dashboard

FieldValue
TemplateDashboard
ReleaseV1
Design dayDay 15 — DES-15
Build taskDSH-05
Route/dashboards/stock
RequirementsSTK-05 · STK-02 · ANA-03
Permissions requireddashboard.view + stock.view. The Valeur du stock card also needs pricing.view.

Purpose. Show stock-outs, low stock, reservations and slow movers at a glance (cahier §4.1, §4.3), so the stock manager knows what to reorder and what is not selling.

Who uses it. Stock manager, super admin, direction. Commercial and delivery managers read it.

Entry points. Pilotage › Stock, the sidebar badge, and stock.alert toasts.

Layout.

  • Filters: Entrepôt (warehouse; « Tous » by default), Marque, Catégorie, and Période (period, used for the flow cards only).
  • KPI cards:
    • Ruptures (same definition as BO-04).
    • Stock bas.
    • Quantité réservée: Σ reserved units, with the number of orders holding reservations.
    • Valeur du stock: Σ physical × purchase price HT. Needs pricing.view.
    • Entrées: units received over the period (RECEIPT, RETURN, and positive ADJUSTMENT / INVENTORY).
    • Sorties: units shipped over the period (SHIPMENT).
    • Couverture inférieure à 7 jours: active variants whose available ÷ average daily shipped quantity over the last 30 days is below 7.
  • Lists:
    • Ruptures à fort impact: out-of-stock variants ranked by quantity sold over the last 30 days.
    • Produits à faible rotation: active variants with physical stock above 0 and no SHIPMENT for 60 days, sorted by quantity.
    • Derniers mouvements: the 10 latest.
  • Chart: Entrées et sorties par jour (paired bars).

Data.

Actions.

  • Each card and list row opens BO-33 with the matching filter, or the variant's movements in BO-34.
  • « Nouvelle réception » opens BO-35 (with stock.create).

Rules.

  • Available = physical − reserved (STK-02).
  • Quantities are a back-office-only view (STK-07). Resellers and guests only see IN_STOCK (En stock) or OUT_OF_STOCK (En rupture), derived from available stock above 0. A low-stock alert here never changes what resellers see.
  • Cancellations and return receptions put quantities back automatically (STK-03). They show in Entrées as RESERVATION_RELEASE (not counted) and RETURN (counted) movements.
  • Thresholds are per variant (and per warehouse when stock is managed per warehouse).
  • Slow movers exclude variants created less than 60 days ago.
  • Without pricing.view, the value card is not rendered and the API leaves the value out.

Real-time. stock.alert refreshes the alert cards and lists and shows the toast described in the overview. order.status_changed (reservations and shipments) refreshes the reserved and flow cards, debounced 10 s.

States.

  • « Aucune rupture » appears as a positive empty state in the success tone.
  • With a single warehouse, the warehouse filter is hidden.

Acceptance criteria.

  • A shipment that brings a variant's available stock to 0 increases Ruptures and lists the variant within 15 s.
  • Valeur du stock is absent from the API response for a user without pricing.view.
  • The Couverture inférieure à 7 jours count matches BO-33 sorted by cover with the same filters.

BO-08 — Finance dashboard

FieldValue
TemplateDashboard
ReleaseV1
Design dayDay 15 — DES-15
Build taskDSH-07
Route/dashboards/finance
RequirementsANA-01 · FIN-05 · FIN-01 · ACL-04
Permissions requireddashboard.view + finance.view

Purpose. Show collections, receivables, overdue amounts and credit exposure (cahier §4.1 « Paiements attendus, crédit utilisé et impayés », §4.7).

Who uses it. Finance manager, super admin, direction.

Entry points. Pilotage › Finance, and the finance cards of BO-04.

Layout.

  • Filters: Période, Wilaya, Commune, Délégué, Mode de paiement (paymentMethod, multi-select of PaymentMethod).
  • KPI cards (amounts TTC, as invoiced):
    • Encaissé: Σ CONFIRMED payments received in the period.
    • Paiements attendus, Impayés, Crédit utilisé: same definitions as BO-04.
    • Déclarations à vérifier: count, amount and oldest age.
    • Factures émises: count and Σ TTC over the period.
    • Avoirs émis: count and Σ TTC of credit notes issued over the period, from returns and after-sales resolutions (FIN-08).
  • Charts:
    • Encaissements par mode de paiement: stacked bars per bucket, over PaymentMethod.
    • Balance âgée: OVERDUE balances in the buckets 1–30, 31–60, 61–90 and over 90 days past the due date.
  • Lists:
    • Principaux débiteurs (top 10): reseller, overdue amount, oldest due date, credit used %, delegate.
    • Déclarations à vérifier: the 5 oldest, each linking to BO-45.

Data.

Actions. Cards and rows open BO-44, BO-45 or BO-46 with the same filters. A debtor row opens the reseller's 360° on its Finance tab.

Rules.

  • Financial data is visible only with finance.view (ACL-04). The page shows no margins.
  • Invoice amounts are TTC, while the revenue on BO-04 and BO-05 is HT. Each card says which one it uses.
  • An invoice is overdue when the daily job has set OVERDUE (due date passed with a balance left).

Real-time. No finance event in V1. The page refreshes every 5 minutes and on focus.

States. « Aucun impayé » appears as a positive empty state. In each chart, an empty bucket shows « 0 ».

Acceptance criteria.

  • The Balance âgée buckets sum to the Impayés card.
  • Confirming a declaration in BO-45 lowers Déclarations à vérifier and raises Encaissé at the next refresh.
  • A user without finance.view gets « Accès refusé » on /dashboards/finance, and the menu item is hidden.

On this page