HOCO B2B
SRS

End-to-end test plan

QA-01 on staging: the fifteen priority scenarios and the 17 October decision scenarios run on the seeded data, step by step per app with the API operations, the cross-app and returns / after-sales runs, the device matrix, the defect scale and daily triage, the hardening checks and the test-data reset.

This page is the plan of QA-01: the whole ecosystem tested end to end on staging with real devices before HOCO's acceptance. The scripts themselves are the acceptance scenarios (objective, requirements, evidence); this page says how the team runs them on staging — which seeded account, product and order each step uses, what is done in which app, which API operations are involved and what must be observed — plus everything the run needs around them: the data reset, the setup after a reset, the device matrix, the defect scale and triage, the hardening report's logged items and the exit report.

ItemDetail
TaskQA-01 — owners Anis (server, back-office, web, triage), Imad (reseller and delegate apps, real devices), Akram (design conformance)
RequirementOPS-05
EnvironmentStaging, a frozen candidate of every app (environments)
DataReference and demo seeds of OPS-01, reset before the run with pnpm qa:reset (reset), then the setup steps
Out of scopeHOCO's own execution on pre-production and the PV (QA-02); the recette seed with the named stores of the acceptance page (prepared in QA-02)

App codes: BO back-office, RW reseller web, RA reseller app, DA delegate app, API a direct call (the API reference).

Entry criteria

  1. SRV-28, MOB-14, QA-03 and WEB-05 are done (the task's dependencies).
  2. Staging runs one tagged build of the API and worker, the back-office and the reseller web; the reseller and delegate apps are staging builds from Firebase App Distribution. The build numbers are written at the top of the execution log and in every defect.
  3. The server suite is green on that build, including the cross-app smoke (pnpm test).
  4. The data was reset the morning of the run (reset) and the setup steps are done.
  5. Staging sends real pushes (Firebase credentials set) — without them pushes are only logged as push (simulated) and every push received check fails. SMS stay simulated (D-13): one-time codes are read from the API log line sms (simulated).
  6. The GitHub labels and the QA-01 milestone exist (defects).

Test data

Everything below is created by the reference seed and the demo seed (packages/db/prisma/seed). The demo seed is deterministic: after a reset the same numbers, names and prices come back, and the dated rows (orders, today's tours) are anchored on the reset day in Africa/Algiers. Business numbers carry the year of the reset (CMD-2026-… in 2026).

Passwords are never written here. Staff, delegates and resellers use the demo password documented in apps/server/MODULES.md; the super administrator's password is SEED_ADMIN_PASSWORD of the staging environment (in the team vault), or the one printed once by the first seed when that variable is empty.

Personas

The acceptance scripts were written for the recette seed (Lina, Nadia, Magasin El Wafa…), which QA-02 prepares. On staging the same roles are played by the demo identities:

Acceptance personaStaging identity (demo seed)Sign-inRole, scope
Lina — super adminSuper administrateuradmin@hoco.localSUPER_ADMIN
Nadia — commercial adminNadia Oualicommercial@hoco.localCOMMERCIAL_ADMIN (orders.validate, CRM, promotions, field)
Rachid — stock managerHakim Bensalemstock@hoco.localSTOCK_MANAGER (stock.validate)
Amel — finance managerSamia Kaddourfinance@hoco.localFINANCE_MANAGER (finance.validate)
Sofiane — delivery managerRafik Hamdanilivraison@hoco.localDELIVERY_MANAGER
Mourad — directionKarim Lounisdirection@hoco.localDIRECTION (read-only)
Yacine — delegate, OranYacine Meziane, DLG-0002+213555000202Zones ORN (wilaya 31) and CST (wilaya 25)
Samir — delegate, AlgerAbdelkader Benmoussa, DLG-0001+213555000201Zone ALG (wilaya 16)
Magasin El Wafa (DEMI_GROS, Oran, credit)Galaxy Phone Aïn El Turck, REV-000008+213555010008DEMI_GROS, wilaya 31, Yacine, credit limit 500 000,00 DA
Phone Center Bahia (DETAIL, limit 0)Mega Phone Ouled Rahmoune, REV-000003+213555010003DETAIL, wilaya 25, Yacine, limit 0
Alger Mobile Store (SUPER_GROS, Alger)Planet Mobile Bouzaréah, REV-000007+213555010007SUPER_GROS, wilaya 16, Abdelkader, limit 0
Bazar El Amel (prospect INTERESTED)City Mobile Aïn Smara, REV-000030— (no account)INTERESTED, declared DETAIL, wilaya 25, Yacine
A sign-up waiting for validationSmart Accessoires Es Sénia, REV-000017+213555010017ACCOUNT_CREATED, validation PENDING
A suspended resellerStar Mobile Beni Hamidène, REV-000021+213555010021SUSPENDED
Phones created during the runAccessoires Nour +213555000204, Téléphonie Belhadj +213555000206, a rejected sign-up +213555000207, Kiosque Test +213555000210—Not in any seed; free after every reset

Other resellers follow the same pattern: REV-0000NN signs in with +2135550100NN. REV-000001 to REV-000012 are ACTIVE, 013–016 FIRST_ORDER, 017–020 waiting for validation (020 rejected), 021–022 suspended, 023–040 prospects. Customer types rotate SUPER_GROS (wilaya 16) → DEMI_GROS (31) → DETAIL (25). Credit limits: REV-000004, 008, 012 and 016 at 500 000,00 DA, the others 0.

Products

Prices are HT per customer type (super-gros · demi-gros · détail); TTC = HT + 19 % VAT, the reseller sees TTC (D-17). Stock is the physical quantity right after a reset (one warehouse).

Role in the scripts (acceptance product)SKU · productPrices HT (DA)StockNotes
The in-stock reference product (HOCO N32 30W)HOCO-C13-BL · HOCO C13 Chargeur secteur 20 W USB-C1 483 · 1 631 · 1 809107TTC 1 764,77 · 1 940,89 · 2 152,71
The promoted BOROFONE cable (BX51)BOROFONE-BX26-1M · BOROFONE BX26 Câble USB-A vers USB-C 3 A409 · 450 · 499385Category Câbles › USB-C; no sale step in the demo data (min 1, step 1)
The HOCO cable outside the promotion (X88)HOCO-U34-BL · HOCO U34 Câble USB vers Lightning 2,4 A347 · 382 · 423282
The low-stock product (ACEFAST T6, 8 units)BOROFONE-BU32-BL · BOROFONE BU32 Câble USB vers Lightning 2,4 A234 · 257 · 2858Below the default threshold 20: a back-office alert only
The daily-offer power bank (J101)HOCO-J55-WH · HOCO J55 Power bank 20 000 mAh 22.5 W2 224 · 2 446 · 2 713329Daily offer created in setup
A large order line (credit tests)HOCO-J52-BL · HOCO J52 Power bank 20 000 mAh 20 W4 387 · 4 826 · 5 352247
The out-of-stock product (CA75)HOCO-Y67-WH · HOCO Y67 Montre connectée5 683 · 6 251 · 6 9330Status OUT_OF_STOCK
The segment-only product (BA21)BOROFONE-BO50-BL · BOROFONE BO50 Casque Bluetooth pliable3 775 · 4 153 · 4 606255Visible to everyone after a reset; restricted in setup
The never-visible product (A55, DRAFT)HOCO-EW49-WH · HOCO EW49 Écouteurs sans fil TWS—515DRAFT; ACEFAST-C27-1M is HIDDEN, two products are ARCHIVED

The catalog holds 60 products (48 ACTIVE) in 8 root categories with 17 attribute definitions (power, ports, connector, current, length, colour, capacity…), so the facets have real values.

Settings, regions, orders and tours

ItemSeeded value
OrderingMinimum 10 000,00 DA TTC; quantities 1–999, step 1 (D-19); the reseller cancels while SUBMITTED or PENDING; confirmation reminder after 4 h
StockReserve at VALIDATED, deduct at SHIPPED (D-22); threshold 20
CreditBLOCK over the limit (D-04); new resellers at 0
Prices before validationShown, declared type (D-18)
Assisted orders72 h to confirm (D-23)
Returns, after-salesReturns 7 days after delivery; claims 180 days (D-35)
FieldGPS tolerance 150 m, accuracy ≤ 100 m, out-of-tolerance check-in refused; positions every 300 s or 200 m; report required to end a visit; unlock 24 h; closing requires sync
Support regionsCentre (wilaya 16 …) +213000000002, Est (25 …) +213000000003, Ouest (31 …) +213000000004, Sud +213000000005, default National +213000000001 — placeholder numbers until D-36
App versionsMinimum and latest 1.0.0 for every app and platform
Not seededPromotions, banners, segments, visibility rules, invitations, carriers — created in setup or during the scenarios

Seeded orders (each status of the lifecycle):

OrderStatusChannelResellerUsed by
CMD-2026-000017 · 000018SUBMITTEDApp · WebREV-000002 · REV-000003The queue is not empty at the start (AT-30, AT-42)
CMD-2026-000019SUBMITTED, assisted, confirmed by the reseller, first orderAssistedREV-000013First-order flag on BO-41
CMD-2026-000012PENDING (credit)App, creditREV-000004AT-17 on-hold row
CMD-2026-000013PENDING, AWAITING_RESELLERAssistedREV-000005AT-18 (one assisted order may wait per reseller)
CMD-2026-000014 · 000015 · 000011VALIDATEDApp · Back-office · AppREV-000006 · REV-000007 · REV-000014AT-35 cancellation of a confirmed order
CMD-2026-000016 · 000010 · 000008 · 000009PREPARING · READY · SHIPPED · SHIPPEDDelivery board
CMD-2026-000004 · 000002 · 000003DELIVERED 6, 10 and 8 days before the resetREV-000016 · REV-000004 · REV-000012000003 for the return-window refusal and the open claim
CMD-2026-000001RETURNED (RET-2026-000001 refunded, credit note AVO-2026-000001)REV-000001Finance views
CMD-2026-000007 · 000005 · 000006REFUSED · CANCELLED (assisted expired) · CANCELLEDHistory

Open after-sales claim TKT-2026-000001 on CMD-2026-000003; invoices FAC-2026-000001…000006 (three unpaid).

Today's tours (the day of the reset): TRN-2026-000001 Abdelkader, IN_PROGRESS, 5 visits; TRN-2026-000002 Yacine, TO_START, 4 visits — REV-000002 (Suivi), REV-000024 (Prospection), REV-000003 (Recouvrement), REV-000026 (Prospection). Seeded positions are random points around the wilaya seat: on-site GPS steps need the setup step S-04.

Resetting the test data

pnpm qa:reset (apps/server/scripts/reset-test-data.ts) brings an environment back to the seeded state above.

StepWhat it does
1. GuardRefuses, listing every reason, unless all of these hold: NODE_ENV and APP_ENV are not production; the database name contains staging, test or local as a word (hoco_staging, hoco-test); neither the database name nor its host mentions prod; HOCO_RESET_CONFIRM equals the database name; REDIS_URL names a database number (redis://redis:6379/3). With --buckets, both bucket names must carry a marker too. The rules are unit-tested (apps/server/src/testing/reset-guard.test.ts).
2. DatabaseDefault: prisma migrate deploy (pending migrations), then one TRUNCATE … RESTART IDENTITY CASCADE of every table except _prisma_migrations — works with an application role that does not own the schema. --migrate-reset: prisma migrate reset --force instead (drops and recreates the schema; needs the schema owner).
3. SeedsThe reference seed, then the demo seed (--no-demo for reference data only).
4. RedisFLUSHDB on the database of REDIS_URL only — rate limits, caches, the SSE replay buffers and the BullMQ queues of HOCO; never FLUSHALL. --skip-redis keeps it.
5. BucketsOnly with --buckets: empties S3_BUCKET_PUBLIC and S3_BUCKET_PRIVATE (uploaded photos, documents, generated PDFs). Without it, files stay in the bucket but no row points to them any more.
# on the staging host, api and worker stopped, with the staging environment loaded
HOCO_RESET_CONFIRM=hoco_staging pnpm qa:reset            # truncate, reseed, flush Redis db
HOCO_RESET_CONFIRM=hoco_staging pnpm qa:reset --buckets  # also empty the staging buckets

Rules:

  • Stop the api and the worker first and restart them after: open connections hold locks during the truncation, and a running worker would refill Redis or dispatch events of rows that no longer exist. Mobile testers sign in again after a reset (sessions are gone; the delegate app's offline store must be empty — finish or discard pending items first).
  • Reset the morning of a run day: the demo seed creates that day's tours and dates the orders from it.
  • Set SEED_ADMIN_PASSWORD in the staging environment, otherwise the super administrator gets a new random password printed once.
  • The script is not meant for pre-production (its name contains prod and is refused): QA-02 loads HOCO's real catalog there.
  • Measured on the development machine against a scratch database: about 25 s for truncation, both seeds and the Redis flush; re-running gives identical counts and numbers (20 orders up to CMD-2026-000019, 40 resellers up to REV-000040, 60 products, 30 users).

Setup after a reset

Done once per reset, by the person named, before the scenarios that need it. Each is also a check of the back-office screen it uses.

#Who (app)ActionAPIFor
S-01Nadia (BO-50)Promotion « -15 % sur les câbles BOROFONE »: PERCENT_OFF 15 %, scope category Câbles + brand BOROFONE, audience wilaya 31, from D 00:00 to D+7 23:59, cap 200 units; activateadmin.promotions.crud, admin.promotions.setStatusAT-03, AT-06, AT-39
S-02Nadia (BO-50)Daily offer on HOCO-J55-WH for D: FIXED_PRICE per customer type, entered HT like every promotional price (« Prix promotionnel (HT) »): e.g. 2 000 · 2 200 · 2 400,00 DA HT, below each type's price (demi-gros 2 200,00 HT = 2 618,00 DA TTC instead of 2 910,74). A fixed price above the type's price never applies (BR-65)SameAT-06
S-03Nadia (BO-11), Hakim (BO-28)Segment Partenaires with REV-000008 and REV-000007; BOROFONE-BO50-BL visible to that segment onlyadmin.segments.crud, admin.products.visibilityAT-02, AT-07a
S-04Nadia (BO-10, BO-23)Move the four stops of TRN-2026-000002 to real points around the test site (≥ 400 m apart), so Yacine's tester can stand inside and outside the 150 m toleranceadmin.resellers.update, admin.tours.setStopsAT-08, AT-09, AT-15
S-05Samia (BO-46)Lower REV-000008's credit limit to 50 000,00 DAadmin.credit.updateAT-17
S-06Lina (BO-66)Check the app-version policies against the installed staging builds (minimum ≤ installed)admin.appVersions.crudEvery app scenario; AT-28 changes them

Run order

Several scripts feed each other (AT-10 → AT-16, AT-11 → AT-12, the order of X-1 → AT-05 → the returns run). One campaign day per block; field blocks run on site.

BlockScenariosOwnerWhere
1. Access and catalogAT-01, AT-02, AT-38, AT-39, AT-40, AT-41, AT-14Anis (BO, RW), Imad (RA)Office
2. Ordering and live statusX-1, AT-03, AT-04, AT-42, AT-43, AT-05, AT-35, AT-17, AT-20Anis, ImadOffice
3. Promotions, supportAT-06, AT-07a, AT-44Anis, ImadOffice
4. Field (on site)AT-08, AT-09, AT-10, AT-13, AT-15, AT-24, AT-11, AT-12, X-2Imad (DA, RA), Anis (BO)On site in Oran or the test site of S-04
5. Returns and after-salesR-1, R-2 (AT-46, AT-45)Anis, ImadOffice
6. RegressionEvery step linked to a fixed defect, then the automated checksAnis, Imad—

Akram reviews the four interfaces against the design handoff during blocks 1–5 and logs conformance issues with the same severity scale.

Priority scenarios on staging

Each scenario keeps the acceptance page's numbering and evidence; the tables below bind it to the staging data and the API. D is the run day.

AT-01 — Reseller account: sign-up with customer type, validation

ItemDetail
PreconditionsNo record with +213555000206 or +213555000207 (true after a reset); Nadia signed in on BO-13
DataNew store Téléphonie Belhadj, wilaya 31 Oran, commune Oran, type Détail; queue already holds REV-000017–000019
APIauth.otp.send, auth.otp.verify, auth.signup.reseller, cart.items.add, admin.resellers.validation.list, admin.resellers.validate, admin.events.stream
#AppActionExpected
1RA« Créer un compte », 0555 00 02 06, no invitation codeCode sent (read it in the API log, sms (simulated)); 60-second resend countdown
2RAWrong code, then the right one« Code incorrect » with 4 attempts left; then the store form
3RAFill the store, type Détail, password; submitPENDING_VALIDATION, « Compte en attente de validation »
4RAOpen HOCO-C13-BL, « Ajouter au panier »Price 2 152,71 DA (détail, D-18 default); the line is added but the cart shows the blocking issue ACCOUNT_PENDING_VALIDATION and checkout answers 403 ACCOUNT_PENDING_VALIDATION (cart rule 5)
5BOBO-13 open, no reloadThe account appears within 5 s, source SELF_SIGNUP, declared Détail
6BONadia approves with type DétailACTIVE; journal VALIDATE with Nadia and the type
7RA—Push « Compte validé » (SMS in the API log); the cart accepts HOCO-C13-BL
8RA + BORepeat 1–3 with +213555000207 on the reseller web (RW-01); Nadia rejects with a reason« Compte non validé » with the reason on RW-01 and in the app if signed in there

AT-02 — Catalog: only authorised products, prices and availability

ItemDetail
PreconditionsSetup S-03 done; REV-000003 (détail) on RA, REV-000008 (demi-gros, segment Partenaires) on RW
APIcatalog.products.list, catalog.products.get, admin.products.setStatus, cart.get
#AppActionExpected
1RAREV-000003 opens the catalogThe 48 ACTIVE and 3 OUT_OF_STOCK products less BOROFONE-BO50-BL; no DRAFT, HIDDEN or ARCHIVED product (HOCO-EW49-WH, ACEFAST-C27-1M absent)
2RAOpens HOCO-C13-BL2 152,71 DA TTC, a recommended price, « En stock » — no quantity anywhere
3RAOpens BOROFONE-BU32-BL (8 units)« En stock » only
4RAOpens HOCO-Y67-WH« En rupture »; add to cart disabled
5RWREV-000008 opens HOCO-C13-BL, then the catalog1 940,89 DA TTC; BOROFONE-BO50-BL listed
6BO + RA/RWHakim hides BOROFONE-BX26-1M after both added it to their cartGone from both catalogs after a refresh; the cart line shows « Produit indisponible »
7APIGET /v1/catalog/products/{id of BOROFONE-BO50-BL} with REV-000003's token404 NOT_FOUND

Restore BOROFONE-BX26-1M to ACTIVE after step 6.

AT-03 — Order: unique number, visible in the back-office, confirmed there

ItemDetail
PreconditionsSetup S-01; REV-000008 on RA; Nadia on BO-41 (queue already shows CMD-2026-000017–000019)
APIcart.items.add, cart.get, cart.checkoutOptions, cart.checkout, admin.orders.toConfirm, admin.orders.validate, events.stream
#AppActionExpected
1RAHOCO-C13-BL × 3Cart 5 822,67 DA TTC; checkout blocked: « Le montant minimum de commande est de 10 000,00 DA » (ORDER_MIN_AMOUNT_NOT_MET)
2RAHOCO-C13-BL × 6 and BOROFONE-BX26-1M × 10Demi-gros prices; -15 % on the BOROFONE cable only; HT, VAT, TTC totals; free delivery (D-20)
3RADefault address, Paiement à la livraison, « Valider la commande »A CMD-2026-NNNNNN number (the sequence continues after the seeded 000019); status « Envoyée »
4BOBO-41, no reloadRow within 5 s, channel RESELLER_APP, controls green
5BOWait two minutesStill SUBMITTED
6BONadia confirmsVALIDATED; the row leaves the queue; the app shows « Validée » live
7RASecond order (HOCO-U34-BL × 30)The next number of the sequence
8RWREV-000008 on the webCart empty (server-side cart); both orders in RW-08

AT-04 — Stock: reservation, deduction, automatic restock

ItemDetail
PreconditionsHOCO-C13-BL: physical 107, reserved 0 after a reset (less what AT-03 reserved: note the values first); Hakim on BO-33 / BO-34
APIadmin.stock.levels, admin.stock.movements, admin.orders.validate, admin.orders.transition, admin.deliveries.update, admin.orders.cancel
#AppActionExpected
1RAREV-000008 orders HOCO-C13-BL × 10Reserved unchanged (reservation at confirmation)
2BONadia confirmsReserved +10, available −10; RESERVATION movement with the order number
3BORafik: En préparation, Prête (packages)Quantities unchanged; delivery note BL-…
4BORafik sets own fleet, driver name and phone, shipsPhysical −10, reserved −10; SHIPMENT movement
5BOA second order × 5 confirmed, then cancelled by Nadia with a reasonReserved back automatically; RESERVATION_RELEASE
6RAThroughoutOnly « En stock »

AT-05 — Tracking: back-office changes live in the app and notified

Run on the order of X-1 (confirmed, still VALIDATED). Data: REV-000008; the app closed on one phone, RW-09 open on the order. API: admin.orders.transition, admin.deliveries.proof, orders.get, orders.document, notifications.list, notifications.markRead, events.stream.

#AppActionExpected
1BORafik: En préparationRW-09 updates within 5 s without reload; push on the closed app's phone; dated inbox entry
2BOPrête, then Expédiée (own fleet, driver, phone)Same three effects each time; tracker steps per the reseller view
3BOProof of delivery (recipient, photo)« Livrée » live and notified; invoice FAC-… downloadable from RA-22 / RW-09 through a short-lived link (worker must run)
4RA + BOCompare the RA-22 timeline with BO-39Same statuses and times (Africa/Algiers)
5RA + RWOpen the push, then the web inboxRead on both; badge decremented

AT-06 — Promotion: only authorised products, dates and targets

#AppActionExpected
1RAREV-000008 (wilaya 31): BOROFONE-BX26-1M × 20, HOCO-U34-BL × 10-15 % on the BOROFONE cable only
2RWREV-000007 (wilaya 16): BOROFONE-BX26-1M × 20No discount; promotion absent from RW-10
3BONadia schedules a second promotion from D+1Programmée, not applied on D
4BO + RAA second promotion on HOCO-U34-BL with a cap of 5 units (an active promotion's cap can only be raised); REV-000008 checks out 205 units discounted, the promotion ends (ENDED, 5/5); later carts show « Offre terminée »
5RAHOCO-J55-WH on DDaily-offer price with the countdown banner
6BOThe submitted order after the promotion endedLines keep the label and discount

AT-07a — V1 variant: promotions only, no loyalty surface

AT-07 (loyalty) is V2 (D-16) and not run. Data: S-03's segment. API: catalog.home, promotions.list, me.get.

#AppActionExpected
1RA, RWHome, account, promotionsNo points, level or rewards; no RW-15 entry
2BO + RAPromotion for segment Partenaires on HOCO-C13-BLREV-000008 gets it; REV-000003 does not
3RATwo promotions on the same productBest discount only (PRM-03); the cart names it
4BOOrder detail, promotion reportThe promotion per line

AT-08 — Tour: start, visits, closing with full summary

ItemDetail
PreconditionsReset on D; S-04 done; Yacine (+213555000202) on site with the DA; Nadia on BO-06
DataTRN-2026-000002, TO_START, stops REV-000002 (Suivi), REV-000024 (Prospection), REV-000003 (Recouvrement), REV-000026 (Prospection)
APIdelegate.tours.today, delegate.tours.start, delegate.tours.reorder, delegate.visits.arrive, delegate.visits.checklist, delegate.visits.addPhoto, delegate.visits.report, delegate.visits.end, delegate.visits.notDone, delegate.tours.closingPreview, delegate.tours.close, admin.live.delegates, admin.tours.get
#AppActionExpected
1DADA-05Today's tour, 4 visits; « Démarrer la tournée » enabled
2DA + BOStartIN_PROGRESS; Yacine on BO-06 within 5 s
3DA + BOSwap stops 2 and 4Accepted (RECOMMENDED); new order on BO-23
4DAREV-000002: arrive, start, checklist, note, photo, report, endDONE, locked
5DAA Prospection stop: end without the façade photoRefused with the missing items; DONE after photo and report
6DAREV-000003: « Visite non réalisée », Magasin ferméNOT_DONE with the reason
7DALast stop done; closing previewDone / not done counts, orders, revenue, kilometres; no blocking item
8DA + BOClose with a commentCOMPLETED; BO-23 full summary; performance updated

AT-09 — GPS: check-in tolerance, consented positions only

Same tour and preconditions as AT-08 (run it on the first stop before AT-08 step 4), on a fresh install of the DA. API: delegate.visits.arrive, sync.locationPings, delegate.tours.suspend, delegate.tours.resume, admin.tours.track, admin.visits.get.

#AppActionExpected
1DAFirst sign-inDA-04 consent text; system prompt; consent recorded
2DAAbout 400 m from the stop, « Arrivé »Refused, distance shown; nothing recorded
3DAIndoors, accuracy above 100 m« Précision insuffisante »
4DA + BOAbout 80 m away, good accuracyAccepted; BO-25 shows position, accuracy, distance, time
5DA + BOScreen off 15 min while movingA position about every 5 min or 200 m on the BO-23 trail; « Tournée en cours » notification
6DASuspend 15 min, resumeNo position during the suspension
7DAAfter closing (AT-08)No position; no location indicator
8DAMock-location app, « Arrivé »GPS_MOCK_LOCATION

AT-10 — Prospect: field record in the CRM without duplicate

Data: Accessoires Nour +213555000204 and Kiosque Test +213555000210 (free after a reset), wilaya 31; Abdelkader (zone ALG) checks across zones. API: delegate.resellers.checkPhone, delegate.resellers.create, delegate.resellers.addPhoto, sync.batch, admin.resellers.create, admin.resellers.checkPhone.

#AppActionExpected
1DA + BOYacine creates Accessoires Nour with GPS and façade photoA new REV- number; on BO-09 with source DELEGATE_APP, delegate Yacine, position on BO-10, photo in documents
2DANour Accessoires with +213 555 00 02 04Duplicate detected before saving
3DAAirplane mode: Kiosque Test; reconnect; force a second replayOne record
4BONadia creates 0555000204409 PHONE_ALREADY_EXISTS with a link
5DAAbdelkader checks 0555000204« Numéro déjà enregistré », no store details

AT-11 — Invitation: QR and link attribute to the right delegate

Data: prospect REV-000030 (City Mobile Aïn Smara, INTERESTED, Yacine); a test phone without the RA. Its phone +213555010030 has no account. API: delegate.invitations.create, delegate.invitations.markShared, invitations.landing, invitations.resolve, invitations.attribute, auth.signup.reseller, admin.invitations.list.

#AppActionExpected
1DADA-16 for REV-000030Code XXXX-XXXX, link on the staging web host /i/…, QR
2DAShare by WhatsAppPrefilled French text with the link; step Invitation envoyée
3RAScan the QR on the test phonePlay Store page (or Firebase App Distribution for the staging build — note which)
4RA + DAInstall, openCode prefilled; DA-16 Application téléchargée within a minute
5RA + DASign up with +213555010030Same REV-000030 claimed; Yacine pushed
6RABrowseCatalogue consulté
7BOInvitation funnelAttributed to Yacine

AT-12 — First order: linked to the reseller and the source delegate

After AT-11, Nadia validates REV-000030 (BO-13). Then run X-2 with REV-000030 instead of REV-000008, and check in addition: the order is flagged Première commande on BO-39, REV-000030 becomes FIRST_ORDER, and the reports first orders by delegate, Yacine's performance and the invitation funnel each count +1. API in addition to X-2: admin.reports.run, admin.delegates.performance.

AT-13 — Visit report: saved, visible, triggers the follow-up

During AT-08 at REV-000002. API: delegate.visits.report, admin.resellers.timeline, admin.tasks.list, delegate.tasks.list, delegate.tasks.complete.

#AppActionExpected
1DAReport: Gérant, interest Élevé, products HOCO-C13-BL and HOCO-J55-WH, the S-01 promotion, issue Prix concurrent, next action Relance téléphonique at D+2Saved; the visit can end
2BOBO-11 of REV-000002The report on the timeline with every field
3BO + DABO-14, DA-19Task Relance téléphonique due D+2, Yacine
4DADue date set to D for the test; 08:00 digest (or the job run by hand)Push « Vos relances du jour »
5DA + BOComplete with an outcomeDONE; interaction on the timeline

AT-14 — Permissions: no access to unauthorised functions

API: admin.payments.list, admin.orders.validate, admin.products.get, admin.roles.updatePermissions, admin.audit.list, delegate.home.

#AppActionExpected
1BOHakim signs in; types the finance URLHis modules only; « Accès refusé »
2APIHakim's session on GET /v1/admin/payments403 PERMISSION_DENIED
3BO + APIKarim (direction) on CMD-2026-000017No confirm button; POST /v1/admin/orders/{id}/validate → 403
4BO + APIRafik opens HOCO-C13-BLNo purchase price in the screen or the response
5BOLina removes orders.validate from COMMERCIAL_ADMINNadia's button gone after refresh; API refuses; journal PERMISSION_CHANGE; restore it
6APIYacine's token on /v1/admin/orders; REV-000008's on /v1/delegate/home403
7—Attach the permission-sweep output of the tested build (apps/server/src/testing/permission-sweep.test.ts)Every route declares a rule; zero unexpected allow

AT-15 — Offline: pending field data syncs when the network returns

On site, during AT-08 (a stop not yet visited) — the QA-01 acceptance « a delegate tour done fully offline syncs without duplicates or lost reports » (SYN-04, SYN-05). API: sync.batch, sync.delta, files.createUpload, files.completeUpload, admin.visits.get.

#AppActionExpected
1DATour started online, then airplane modeTour, visits and points of sale available
2DAFull visit (GPS check-in, checklist, note, two photos, report, end); create a prospectSaved locally; DA-27 pending count
3DAKill and reopen the appPending items kept
4DANetwork backReplay in order; « Synchronisé » within a minute; photos uploaded
5BOVisit recordoccurredAt (device) and a later receivedAt; no duplicate
6DACut the network mid-sync, restoreNo duplicate; queue resumes
7DA + BOWhile offline, Nadia edits the same prospect; Yacine tooConflict in DA-27 with an explanation

Then AT-24 on the same tour: one stop left without a status while offline blocks the closing (« 1 élément non synchronisé »), the API answers 409 TOUR_CLOSING_BLOCKED, and closing works after the reason and the sync.

Decision scenarios (17 October 2026)

The team decisions the QA-01 task lists, with the scenario that proves each. AT-15 / AT-24 (offline tour) are run with the field block above; AT-45 and AT-46 are the returns and after-sales run.

DecisionScenario
Guest browsing without pricesAT-38
Three customer-type pricesAT-39
Advanced filters with facetsAT-40
Availability-only stockAT-41
Manual confirmation of every orderAT-42
Live status for the reseller (SSE + push)AT-43, X-1
Regional WhatsApp routingAT-44
Cancellations restock automaticallyAT-35
Returns with automatic restock and credit noteR-1 (AT-46)
After-sales claimsR-2 (AT-45)
Delegate's offline tour with later syncAT-15, AT-24

AT-38 — Guest browsing without prices

API: catalog.products.list, catalog.products.get, catalog.facets, cart.items.add (no session).

#AppActionExpected
1RA, RWFresh install → « Découvrir le catalogue »; RW without signing inHome, catalog, product pages, promotions open
2RA, RWHOCO-C13-BLNo price; lock « Créez un compte pour voir les prix »
3RA, RW« Ajouter au panier »Sign-up prompt; nothing added (401 on the cart)
4RA, RWFiltersNo price range; the other filters work
5APIGET /v1/catalog/products and /v1/catalog/facets without a sessionprice: null, priceLocked: true, availability only; no price facet
6RASign up from the lock (a free test phone, e.g. +213555000211)Prices of the declared type right away (D-18)

AT-39 — Correct price per customer type

Data: REV-000007 (super-gros), REV-000008 (demi-gros), REV-000003 (détail). API: catalog.products.get, cart.items.add, admin.resellers.update, profile.update.

#AppActionExpected
1RA, RWThe three open HOCO-C13-BL1 764,77 · 1 940,89 · 2 152,71 DA TTC
2RA, RWEach adds 10Line totals 17 647,70 · 19 408,90 · 21 527,10 DA TTC; HT and VAT consistent on order and invoice
3RAREV-000008 adds BOROFONE-BX26-1M (S-01)-15 % on top of the demi-gros price; REV-000003 (wilaya 25) gets no discount
4BO + RANadia changes REV-000003 to Demi-grosJournal entry; « Vos tarifs ont changé »; cart re-priced at the next read; submitted orders unchanged
5RAREV-000003 edits the type in the profileNot editable — « Contactez HOCO »

Restore REV-000003 to Détail afterwards.

AT-40 — Advanced filters with facets

API: catalog.facets, catalog.products.list, admin.attributes.crud, admin.variants.crud.

#AppActionExpected
1RACategory CâblesSub-categories (USB-C, Lightning, Micro-USB, 3-en-1), brands and attributes with counts
2RAConnector USB-C + length 1 m + brand BOROFONEBOROFONE-BX26-1M among the results; counts of the other values updated
3RAEn promotion + a price range (signed in)Results and counts consistent; the range uses the user's prices
4RAFacet count vs number of resultsEqual for every value tried
5BO + RAHakim adds a length value (e.g. 1,5 m) and sets it on a variant (BO-71)The value appears with its count
6RWSame filters in the sidebar (RW-03)Same results

AT-41 — Stock quantities never shown to resellers

Data: BOROFONE-BU32-BL (8) and HOCO-Y67-WH (0). API: every reseller and delegate read used in blocks 1–4; the exposure sweep of the tested build.

#AppActionExpected
1RA, RW, DACatalog, product, cart, order and promotion screens; DA-17 assisted order« En stock » / « En rupture » only; never a number or « Plus que … »
2RABOROFONE-BU32-BL × 12422 STOCK_INSUFFICIENT with availability only; the message does not reveal 8
3APIInspect the JSON of those operations (browser dev tools on RW; a proxy for the apps)No stock-quantity field
4BOHakim (BO-33)Physical, reserved, available visible

AT-42 — Every order needs a back-office confirmation

API: cart.checkout, delegate.assisted.submit, orders.confirmAssisted, admin.orders.create, admin.orders.toConfirm, admin.orders.transition, admin.orders.validate.

#AppActionExpected
1RA, RW, DA + RA, BOOne order per channel for REV-000008: app, web, assisted (confirmed on RA), back-office on behalf (Nadia)All four in BO-41 as SUBMITTED (or PENDING on hold), oldest first, with their controls, beside the seeded CMD-2026-000017–000019
2BOWait 10 minutesNone leaves the queue alone
3BO + APIRafik moves one to En préparationNot offered; the API answers 409 ORDER_TRANSITION_NOT_ALLOWED (or 403)
4BONadia confirms eachVALIDATED, reservation made, on the delivery board (BO-42)
5BOOne left past 4 h (orders.confirmationReminderHours)Reminder « Commandes en attente de confirmation » to the confirmers

AT-43 — Live order status on the reseller screen

Run with X-1 and AT-05. API: events.stream, orders.get, orders.list.

#AppActionExpected
1RARA-22 open; Nadia confirmsStatus changes within 5 s, no pull-to-refresh
2RWRW-09 and RW-08 openBoth update live
3RA, RWEach later statusLive and push and inbox entry
4RAAirplane mode 1 min, status changed, network backCurrent status on reconnection (replay with Last-Event-ID)
5RAApp in background during a changePush; screen current on opening

AT-44 — Regional WhatsApp number

API: support.whatsapp, admin.supportRegions.crud.

#AppActionExpected
1RAREV-000008 (wilaya 31) taps WhatsAppOuest +213000000004, prefilled text with the store name and REV-000008
2RWREV-000007 (wilaya 16)Centre +213000000002
3RAGuest with location allowed (in Oran)Ouest; the position is not stored (API log and database)
4RA, RWGuest, location refusedWilaya picker; Alger → Centre; remembered
5RAPicker dismissedNational +213000000001
6BO + RANadia moves wilaya 31 to Centre (BO-70)REV-000008's button uses +213000000002 at once; move it back
7RAOutside the region's hoursHours shown; WhatsApp still opens

AT-35 — Cancellation rules and automatic restock

Data: a fresh SUBMITTED order of REV-000008; seeded CMD-2026-000014 (VALIDATED, REV-000006) and CMD-2026-000008 (SHIPPED). API: orders.cancel, admin.orders.cancel, admin.stock.movements.

#AppActionExpected
1RACancel the SUBMITTED order with a reasonCANCELLED; staff notified; no stock movement
2RA + APIREV-000006 on CMD-2026-000014No cancel button; 409 ORDER_TRANSITION_NOT_ALLOWED
3BONadia cancels CMD-2026-000014 with a reasonReserved quantities released automatically (RESERVATION_RELEASE); REV-000006 notified live and by push
4BOCancel CMD-2026-000008Refused — a return only

Complementary scenarios in the run

Two complementary scripts run in block 2 because the order controls they exercise sit on every order path.

AT-17 — Credit check blocking and approval

Data: S-05 (REV-000008 limit 50 000,00 DA, nothing open); seeded CMD-2026-000012 (REV-000004, PENDING on credit). API: cart.checkoutOptions, cart.checkout, admin.settings.update, admin.orders.validate, admin.credit.get.

#AppActionExpected
1RAREV-000008: HOCO-J52-BL × 11 (63 172,34 DA TTC), CréditRefused, « Crédit disponible : 50 000,00 DA »; cash on delivery works
2BO + RALina sets credit.overLimitPolicy to REQUIRE_APPROVAL; same checkoutOrder PENDING with the credit control failed, in BO-41
3BOSamia (finance, no orders.validate) opens BO-41Sees the orders pending only for credit (this one and CMD-2026-000012), confirms with an override and a reason
4BOJournalOVERRIDE with Samia and the reason
5RAREV-000003 (limit 0) opens the payment methodsCrédit not offered

Restore BLOCK afterwards.

AT-20 — Stock override with permission

Data: BOROFONE-BU32-BL (8 units). API: cart.items.add, admin.orders.create, admin.orders.validate, admin.stock.levels.

#AppActionExpected
1RAREV-000008 adds × 12422 STOCK_INSUFFICIENT
2BONadia creates the same order on behalf of REV-000008Created; stock control failed, PENDING
3BONadia (no stock.validate) confirmsRefused with the stock message
4BOHakim (stock.validate) confirms with an override and a reasonVALIDATED; available −4 in red on BO-33; journal OVERRIDE

Cancel that order afterwards (the reservation is released).

Cross-app checks

The two paths the QA-01 task names. Their API path is also automated (below); on staging they are run by hand across the real apps.

X-1 — Order from the app, confirmed in the back-office, live on the app and the web

ItemDetail
PreconditionsREV-000008 signed in on a phone (RA, RA-22 later) and in a browser (RW, RW-09); Nadia on BO-41; the worker runs (it dispatches the outbox to the streams and pushes)
APIcart.checkout → admin.events.stream (order.to_confirm) → admin.orders.toConfirm → admin.orders.validate → events.stream (order.status_changed) on both reseller sessions → orders.get
#AppActionExpected
1RAHOCO-C13-BL × 6, checkout, cash on deliverySUBMITTED, channel RESELLER_APP, « Envoyée »; the app stays on RA-22
2RWOpen the same order on RW-09« Envoyée », awaiting HOCO's confirmation
3BOBO-41 openRow within 5 s, highlighted, as a new order (order.to_confirm, kind NEW)
4—Wait 2 minutesNothing changes
5BONadia confirmsVALIDATED; the row leaves BO-41 on every open back-office screen
6RA, RWWatch both screens« Validée » on both within 5 s without refresh; push on the phone; one inbox entry, read on both once opened
7BOBO-39 historySUBMITTED → VALIDATED, Nadia, time; RESERVATION movement

Keep this order: AT-05 continues it to Livrée, and the returns run uses it.

X-2 — Assisted order, reseller confirmation, back-office confirmation

ItemDetail
PreconditionsYacine on the DA; REV-000008 on RA (phone) and RW; Nadia on BO-41. One assisted order may wait per reseller (ASSISTED_ORDER_ALREADY_PENDING): REV-000008 has none after a reset
APIdelegate.assisted.create → delegate.assisted.submit → orders.get → orders.confirmAssisted (or orders.rejectAssisted) → admin.orders.toConfirm → admin.orders.validate → delegate.orders.get
#AppActionExpected
1DADA-17 for REV-000008: HOCO-C13-BL × 4, HOCO-J55-WH × 2Demi-gros prices; below 10 000,00 DA the send button is disabled with the reason
2DA« Demander la confirmation »PENDING, AWAITING_RESELLER, channel DELEGATE_ASSISTED, a number; expiry D+72 h
3BOBO-41Listed as an assisted order awaiting the reseller; no confirm action (the API answers 409)
4RAPush and SMS (API log); RA-23Lines, totals, expiry; Confirmer / Refuser
5RWConfirm on RW-09 instead of the appSUBMITTED; RA-23 on the phone no longer offers confirmation; Yacine pushed
6BOBO-41The order now waits for the back-office (SUBMITTED, confirmed by the reseller)
7BONadia confirmsVALIDATED
8RA, RW, DA—« Validée » live on RA and RW; the order in Yacine's list as confirmed
9DA + RARepeat 1–4, then the reseller refusesConfirmation REJECTED_BY_RESELLER, order CANCELLED (ASSISTED_REJECTED); Yacine notified

Returns and after-sales run

R-1 — Return request, reception, stock and credit note (AT-46)

ItemDetail
PreconditionsThe X-1 order delivered by AT-05 (today, inside the 7-day window); its invoice issued; CMD-2026-000003 delivered 8 days before the reset (outside the window); the worker runs (the credit note is issued on return.received)
APIreturns.create, returns.get, admin.returns.list, admin.returns.decide, admin.returns.receive, admin.stock.movements, admin.invoices.get, invoices.get
#AppActionExpected
1RARA-40 on the X-1 order: 3 × HOCO-C13-BL, reason, photoRET-… REQUESTED; in BO-69
2RAA return on CMD-2026-000003 (as REV-000012)Refused with the 7-day window
3RAMore units than delivered422 RETURN_QTY_EXCEEDS_DELIVERED
4BONadia approvesAPPROVED, then En retour; reseller notified live
5BOHakim receives: 3 received, 3 accepted, 2 restocked, 1 damagedRECEIVED; physical +2 automatically (RETURN movement); no manual adjustment
6BO + RAFinance and the reseller's invoicesCredit note AVO-… for the accepted lines; invoice balance reduced; return REFUNDED; reseller notified
7BOHOCO-C13-BL on BO-33 / BO-34Values match steps 5–6

R-2 — After-sales claim to a replacement order (AT-45)

ItemDetail
PreconditionsA delivered order of REV-000008 (X-1); the seeded open claim TKT-2026-000001 (REV-000012, CMD-2026-000003) for the rejection case
APItickets.create, tickets.get, admin.tickets.list, admin.tickets.update, admin.tickets.resolve, admin.orders.toConfirm
#AppActionExpected
1RARA-33: claim on the X-1 order, 2 × HOCO-C13-BL Produit défectueux, photosAFTER_SALES ticket TKT-… with the lines; staff notified
2BONadia diagnoses and resolves RemplacementA free replacement order, linked to the ticket, enters BO-41 and follows confirmation, preparation and delivery; reseller notified with its number
3BOSecond claim on the same order, resolved RetourA return born APPROVED, linked; continue as R-1 step 5
4BOThird claim, resolved AvoirCredit note without a physical return; finance view updated
5BOTKT-2026-000001 resolved Refusée with a reasonREJECTED; REV-000012 notified with the reason
6RAA claim for more units than deliveredRefused
7RWRW-14 for REV-000008The tickets, return and credit note of this run, same statuses as BO-56 / BO-69

Device matrix

Tiers of NFR-04 (proposed; the pilot users' phones are surveyed at the cadrage — replace the models with the surveyed ones when known). Each row lists what is run on it; a device is used with the staging build of the app.

ClassDevice (Android)AppsScenariosConditions
Mid-range, tier 1Samsung Galaxy A15 or A16 (Android 14–15)RA, DAEvery RA and DA step of blocks 1–5Wi-Fi and 4G; battery saver on for one AT-09 run
Mid-range, tier 1Xiaomi Redmi Note 13 or Redmi 13C (HyperOS)DA (main delegate phone), RAAT-08, AT-09, AT-15, AT-24 on site; AT-43HyperOS battery management left at its default: background location and the persistent notification must survive 15 min screen off
Mid-range, tier 1The model most used by HOCO's delegates (from the survey)DAAT-08, AT-15On site
Low-end, tier 2A 2–3 GB RAM phone on Android 10–11RA, DAAT-02, AT-03, X-1, AT-43, AT-15 steps 1–4, AT-37 timings3G / weak 4G (4G-DZ profile of performance); app killed by the system and reopened
Low-end, tier 2An Oppo or Realme A-series (ColorOS)DAAT-09 step 5, AT-15Aggressive battery saver
Minimum SDKEmulators: Android 7.0 (RA minSdk 24), Android 8.0 (DA minSdk 26)RA, DAInstall, sign-in, catalog, checkout (RA); tour start, visit, offline replay (DA)Emulator only — no GPS realism
Browsers (BO)Chrome and Edge (last two), Firefox; 1 024, 1 280, 1 920 px; a 10-inch tabletBOEvery BO step; AT-30—
Browsers (RW)Chrome, Firefox, Safari 16.4+ (when a Mac or iPhone is available); Chrome for Android and Samsung Internet on the tier-1 phonesRWAT-02, AT-03, X-1, X-2 step 5, AT-38–AT-40, AT-43, AT-44360 px to 1 920 px

iOS is out of the run unless D-10 changes. Each defect names the device, OS version and app build.

Defects: severity and daily triage

Severity scale

QA-01 uses three levels. Blocking is the cahier's anomalie bloquante — the recette's Blocker and Critical (severities) — so a defect keeps its meaning when QA-02 starts.

SeverityLabelDefinitionExamplesRule
Blockingsev:blockingA priority or decision scenario cannot pass: a crash, a step that fails with no acceptable workaround, data loss, another user's data visible, wrong amounts, prices, stock or permissions, live status not updatingCheckout impossible; offline visits lost on sync; wrong customer-type price; stock not reserved at confirmation; a reseller sees a stock quantityFixed and redeployed within 1 working day (2 for an amount, stock or permission defect needing a migration); QA-01 cannot exit with one open
Majorsev:majorA function fails with a workaround, or a complementary scenario failsA report export failing; a facet count off by one; a push missing while the live update worksAn owner and a fix date at triage; fixed before the QA-02 entry or listed as a reservation
Minorsev:minorCosmetic, wording, alignment, design conformance with no functional impactTruncated label; wrong icon; spacing off the designBacklog; deferred to LCH-04 with HOCO's agreement when the window is short

Defect report and board

Each defect is a GitHub issue in the repository that owns the fix (hoco for server, back-office and web; hoco-mobile for the apps) with the milestone QA-01 and the labels qa-01 and one sev: label. Body: scenario and step (X-2 #5), environment and build numbers (API, back-office, web, app version and build), device and OS or browser, account, steps to reproduce, expected and actual result, evidence (screenshot or video, the requestId of the failing call from the error envelope or the API log), proposed severity. Statuses follow the defect report: Nouveau, Confirmé, En correction, Corrigé dans le build …, Vérifié, Fermé, Rejeté, Reporté.

Daily triage

Every campaign day at the end of the sessions, 30 minutes, led by Anis with Imad and Akram:

  1. New issues: reproduce (or ask for the missing evidence), confirm or change the severity, assign Anis (server, back-office, web) or Imad (apps), set a fix date for blocking and major. Duplicates closed with a link.
  2. Blocking first: every blocking defect has its fix in progress before anything else is picked; a blocking defect older than its fix time is raised at the next stand-up.
  3. Fixed issues: the fix lands on dev, staging is redeployed (mobile: a new staging build on Firebase App Distribution), the issue moves to Corrigé dans le build N.
  4. Verification next morning: the reporter re-runs the failed step and its scenario on build N; Vérifié → Fermé, or back to En correction with the new evidence.
  5. Minor items are batched; the ones that will not make it are moved to LCH-04 (Reporté) with HOCO's agreement, never silently.
  6. The day's counts (open by severity, opened, closed) go into the execution log summary.

Hardening checks logged for QA-01

The findings the hardening report logged for QA-01, each turned into a check on staging with an owner. They are tracked like defects (issue per item, label hardening), with the severity of the report mapped to the scale above only if the check fails.

#Report severityCheck on stagingPasses whenOwner
L-01HighRun apps/server/load/catalog.js (k6) against staging with the production-sized copy; record p50 / p95 of catalog.products.list, catalog.facets (cold and cached), catalog.products.suggestp95 under 300 ms (NFR-01); otherwise the snapshot cache or SQL paging of the report is scheduled before QA-02Anis
L-02MediumEXPLAIN ANALYZE of the q search on staging; AT-40 search timingsTrigram indexes used, or the measured time accepted against NFR-01Anis
L-03MediumTime delegate.orders.list, admin.products.list, admin.stock.levels, admin.orders.toConfirm on staging during blocks 2 and 4 (BO network tab, API log)Within AT-37's 2 s render budget at staging volumesAnis
L-04MediumInspect the CSP of the back-office and web responses (two reloads of the same page); browse both apps with the console openscript-src carries a nonce that changes on every load and 'strict-dynamic', no 'unsafe-inline' or 'unsafe-eval'; no CSP violation in the consoleAnis
L-05MediumSign in on production and staging hosts in the same browser (once production exists), or check the cookie name and domain on stagingThe staging session cookie is not sent to production hosts and vice versa (environment-specific name or separate parent domain)Anis
L-06LowR-1 with a delivery manager deciding a return IN_TRANSITAllowed per the matrix, or the matrix amendedAnis
L-07LowBO reseller form « Localiser » button on stagingRemoved, or SEC-0.4 amended; no console errorAnis
L-08LowUpload a photo with EXIF GPS data in AT-10 and AT-15; download the stored fileRe-encoded without EXIF and with a thumbnail once SRV-08 lands; until then recorded as openAnis
L-09LowMany guests behind one address (shared Wi-Fi) browsing during AT-38Decision recorded with the pilot traffic (limit or none)Anis
L-10InfoFrom outside the VPS, reach the API port directlyRefused; only the reverse proxy reaches it (SEC-0.10)Anis
L-11Lowpnpm audit --prod on the tested buildNo high or criticalAnis
L-12InfoCI gitleaks job on the tested commitGreenAnis
L-13InfoThe SEC-0.3 CI grep rule with the allow-listThe files of the 12 reviewed $queryRawUnsafe calls allow-listed; no new hitAnis
L-14Infopg_stat_statements on the staging databasePreloaded; the slow-query view available (NFR-8.4)Anis
L-15InfoCookie flags on staging over HTTPSHttpOnly, Secure, SameSite=Lax, the staging domainAnis

The other VPS items of the report (the three k6 scripts, the restore drill, TLS and headers, monitoring) belong to OPS-02 and AT-37 / AT-47; their results are attached to the exit report.

Automated checks

Run on the tested build before the run and again before the exit report:

CheckCovers
pnpm test (Vitest, server suite)Every module suite, the permission, scope and exposure sweeps, the hardening tests
apps/server/src/testing/cross-app-smoke.test.tsThe API path of X-1: an order placed with a reseller-app session waits in admin.orders.toConfirm (and on Nadia's /v1/admin/events stream as order.to_confirm), is confirmed by the commercial admin, and reaches the same reseller's app and web live streams as order.status_changed → VALIDATED; both sessions then read VALIDATED. The API path of X-2: a delegate draft sent for confirmation cannot be confirmed by the back-office first (409), the reseller confirms it with a web session, the back-office confirms it, and the delegate's stream, the delegate, the reseller and the back-office all read VALIDATED. It creates its own product and stock, uses REV-000011 and removes everything it creates; it skips itself without the demo seed
apps/server/src/testing/reset-guard.test.tsThe refusal rules of pnpm qa:reset
Playwright suite (back-office, reseller web)Named by the QA-01 tests; not in the repository yet — added with the web tasks, else the manual run stands

The smoke test replays the outbox events itself (as the worker's dispatcher would) because the test process runs no worker; staging proves the real dispatch.

Execution log

Filled during the run (one row per execution; a re-run after a fix adds a row). Result: Passé, Passé avec réserve, Échoué, Non exécuté (reason).

Builds under test: API … · back-office … · web … · reseller app … (build …) · delegate app … (build …) · reset at …

20 October 2026 — API run on the test sandbox (dev at ab6bdbb, demo seed of the same day, no reset): every office scenario marked API ✓ was driven through the API with the staging identities and behaved as expected; the screen run (BO, RW, RA) is still to do. Defects found and fixed before the run: staff password-reset codes sent by SMS instead of e-mail, test numbers refused on staging (#48); the mock API down (#47). Three steps of this plan contradicted the contract and were corrected (AT-01 step 4, S-02, AT-06 step 4).

IdScenarioOwnerAppsDateDevice / browserResultDefects
AT-01Sign-up, validationAnis, ImadRA · RW · BO2026-10-20API (sandbox)API ✓ — steps 1–8; OTP read in Mailpit
AT-02Catalog visibility, prices, availabilityAnis, ImadRA · RW · BO2026-10-20API (sandbox)API ✓
AT-03Order number, queue, confirmationAnis, ImadRA · BO2026-10-20API (sandbox)API ✓
AT-04Reservation, deduction, restockAnisBO · RA2026-10-20API (sandbox)API ✓
AT-05Live tracking and notificationsAnis, ImadBO · RA · RW2026-10-20API (sandbox)API ✓ — push not checked (no device)
AT-06Promotion targetingAnis, ImadBO · RA · RW2026-10-20API (sandbox)API ✓
AT-07aNo loyalty surface in V1Anis, ImadRA · RW · BO2026-10-20API (sandbox)API ✓
AT-08Tour start to closingImad, AnisDA · BO—
AT-09GPS tolerance and consentImadDA · BO—
AT-10Field prospect without duplicateImad, AnisDA · BO—
AT-11Invitation attributionImad, AnisDA · RA · BO—
AT-12First order and source delegateImad, AnisDA · RA · BO—
AT-13Visit report and follow-upImad, AnisDA · BO—
AT-14PermissionsAnisBO · API2026-10-20API (sandbox)API ✓
AT-15Offline tour and sync (with AT-24)ImadDA · BO—
AT-17Credit blocking and approvalAnis, ImadRA · BO2026-10-20API (sandbox)API ✓ — BLOCK policy only
AT-20Stock overrideAnisBO—
AT-35Cancellation and restockAnis, ImadRA · BO2026-10-20API (sandbox)API ✓
AT-38Guest without pricesAnis, ImadRA · RW2026-10-20API (sandbox)API ✓
AT-39Price per customer typeAnis, ImadRA · RW · BO2026-10-20API (sandbox)API ✓
AT-40Filters and facetsAnis, ImadRA · RW · BO—
AT-41No stock quantity for resellersAnis, ImadRA · RW · DA2026-10-20API (sandbox)API ✓
AT-42Every order confirmed in the back-officeAnis, ImadAll2026-10-20API (sandbox)API ✓
AT-43Live status on the reseller screensAnis, ImadBO · RA · RW—
AT-44Regional WhatsAppAnis, ImadRA · RW · BO2026-10-20API (sandbox)API ✓
X-1App order → BO confirmation → live app and webAnis, ImadRA · RW · BO—
X-2Assisted → reseller → BO confirmationAnis, ImadDA · RA · RW · BO—
R-1Return → reception → stock and credit note (AT-46)Anis, ImadRA · BO2026-10-20API (sandbox)API ✓
R-2Claim → replacement order (AT-45)Anis, ImadRA · RW · BO2026-10-20API (sandbox)API ✓ — steps 1, 2, 5, 6
L-01–L-15Hardening checksAnisAPI · OPS—

The other complementary scenarios (AT-16, AT-18, AT-19, AT-21–AT-23, AT-25–AT-34, AT-36, AT-37, AT-47) run in QA-03 and QA-02 with the acceptance page's scripts; a failure found there on staging is logged on the same board.

Exit report

Attached to the plan before QA-02 starts. It holds:

  1. The builds tested and the reset time; the device matrix as actually run.
  2. The execution log above, with every priority scenario Passé on staging across the apps involved (OPS-05).
  3. Open defects by severity: zero blocking; every major with an owner and a fix date; minor items with their LCH-04 decision.
  4. The confirmation that every order path (app, web, assisted, back-office) waited for the back-office and that the reseller saw each status live (ORD-14, ORD-15) — X-1, X-2, AT-42, AT-43.
  5. The offline tour result: synced without duplicates or lost reports (SYN-04, SYN-05) — AT-15, AT-24.
  6. Akram's conformance review with no open blocking UI issue.
  7. The hardening checks L-01 to L-15 with their outcome, and the k6 and restore-drill results from OPS-02.
  8. The automated checks green on the tested build, and the regression run of every fixed defect.
  9. Readiness for the recette: what QA-02 inherits (open majors, accepted reservations, known issues list).

On this page

Entry criteriaTest dataPersonasProductsSettings, regions, orders and toursResetting the test dataSetup after a resetRun orderPriority scenarios on stagingAT-01 — Reseller account: sign-up with customer type, validationAT-02 — Catalog: only authorised products, prices and availabilityAT-03 — Order: unique number, visible in the back-office, confirmed thereAT-04 — Stock: reservation, deduction, automatic restockAT-05 — Tracking: back-office changes live in the app and notifiedAT-06 — Promotion: only authorised products, dates and targetsAT-07a — V1 variant: promotions only, no loyalty surfaceAT-08 — Tour: start, visits, closing with full summaryAT-09 — GPS: check-in tolerance, consented positions onlyAT-10 — Prospect: field record in the CRM without duplicateAT-11 — Invitation: QR and link attribute to the right delegateAT-12 — First order: linked to the reseller and the source delegateAT-13 — Visit report: saved, visible, triggers the follow-upAT-14 — Permissions: no access to unauthorised functionsAT-15 — Offline: pending field data syncs when the network returnsDecision scenarios (17 October 2026)AT-38 — Guest browsing without pricesAT-39 — Correct price per customer typeAT-40 — Advanced filters with facetsAT-41 — Stock quantities never shown to resellersAT-42 — Every order needs a back-office confirmationAT-43 — Live order status on the reseller screenAT-44 — Regional WhatsApp numberAT-35 — Cancellation rules and automatic restockComplementary scenarios in the runAT-17 — Credit check blocking and approvalAT-20 — Stock override with permissionCross-app checksX-1 — Order from the app, confirmed in the back-office, live on the app and the webX-2 — Assisted order, reseller confirmation, back-office confirmationReturns and after-sales runR-1 — Return request, reception, stock and credit note (AT-46)R-2 — After-sales claim to a replacement order (AT-45)Device matrixDefects: severity and daily triageSeverity scaleDefect report and boardDaily triageHardening checks logged for QA-01Automated checksExecution logExit report