Promotions & packs
Daily offers, scheduled promotions and temporary discounts on products, categories or brands, targeting, stacking rules, dates and caps applied by the cart, banners with countdown, flash sales and the promotion tabs, and the V2 profitable packs and exclusive offers.
This area covers the commercial offers that stimulate orders (cahier §4.6, §5, §5.5, §12, §14.1, §14.2, reference screens 1 and 7): promotions defined in the back-office (BO), shown in the reseller app and web (RA, RW) — guests included, without prices — and applied automatically by the cart; home banners and the daily-offer carousel; and, in V2, profitable packs. The server's promotions module owns Promotion, PromotionScope, PromotionAudience and Banner (V2 Pack, PackItem); the cart module applies promotions through the price resolver (PRC-02). Contracts: API › Promotions and API › Back-office promotions; statuses on Lifecycles › Promotion.
| ID | Requirement | Release | Priority | Source | Apps |
|---|---|---|---|---|---|
PRM-01 | Simple promotions: daily offers, scheduled promotions, temporary discounts on products, categories or brands | V1 | MUST | §4.6, §14.1 | API · BO · RA · RW |
PRM-02 | Targeting by wilaya, segment, level, brand or volume | V1 | MUST | §4.6 | API · BO |
PRM-03 | Configurable stacking rules and priorities | V1 | SHOULD | §4.6 | API · BO |
PRM-04 | A promotion respects its dates, stock cap and target; the cart total applies it correctly | V1 | MUST | §5.5, §12 | API |
PRM-05 | Home banners and daily offer carousel with countdown | V1 | SHOULD | §5, reference screens 1 and 7 | API · BO · RA · RW |
PRM-06 | Profitable packs: bundles of references at a pack price, added to cart as a unit | V2 | SHOULD | §4.6, §5.5, §14.2 | API · BO · RA · RW |
PRM-07 | Flash sales (2 h to 48 h) with countdown and the promotion tabs Promotions / Flash Sales | V1 | SHOULD | Client doc 03 (18 Oct 2026) | API · BO · RA · RW |
PRM-08 | Exclusive offers by loyalty level and their tab | V2 | COULD | Client doc 03 (18 Oct 2026) | API · BO · RA · RW |
Requirements
PRM-01 — Simple promotions: daily offers, scheduled promotions, temporary discounts
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V1 | MUST | cahier §4.6, §14.1 | API · BO · RA · RW | SRV-19 · DSH-09 · WEB-05 · MRA-06 · DES-10 · DES-19 |
Statement — The system shall let the back-office create daily offers, scheduled promotions and temporary discounts on products, variants, categories or brands, and show them in the reseller apps.
Rules
- A
Promotionhas a name and a reseller-facing label (« Offre flash -30 % »), a type (PromotionType:PERCENT_OFFin basis points,AMOUNT_OFFin HT centimes per unit,FIXED_PRICEHT per unit,FREE_DELIVERY), a scope (PromotionScope: products, variants, categories with their sub-categories, brands), an audience (PRM-02), start and end times, an optional quantity cap (PRM-04), a priority and a stackable flag (PRM-03), an optional image and a daily offer flag. - Lifecycle (
PromotionStatus):DRAFT→SCHEDULED(validated, start in the future) →ACTIVE(automatically at the start, by the scheduler job) →PAUSED↔ACTIVE→ENDED(end time passed or cap reached) →ARCHIVED. Status changes needpromotions.validate; editing needspromotions.update. AnACTIVEpromotion may have its end extended or be paused; its type, value and scope are not editable (duplicate it instead). - Validation: end after start; value within bounds (
PERCENT_OFF1–9 000 bp,AMOUNT_OFFandFIXED_PRICEgreater than 0); a scope that is not empty. A promotion whose price would fall below the purchase price of a scoped variant for some customer type is refused unless the user holdspricing.viewand confirms it (D-01 default). - Display: RA-24 / RW-10 list active promotions and daily offers (
promotions.list), RA-25 the detail with conditions, dates, cap status (« Bientôt épuisée » from 80 % consumed — never a remaining quantity) and eligible products (promotions.get); cards show the promotional price next to the normal one. Guests see promotions and eligible products without any price. - There is no coupon code in V1: eligible promotions apply automatically in the cart.
| Value | UI label (fr) | Meaning |
|---|---|---|
PERCENT_OFF | Remise en % | |
AMOUNT_OFF | Remise en montant | |
FIXED_PRICE | Prix promotionnel | |
FREE_DELIVERY | Livraison offerte |
Acceptance criteria
- A
PERCENT_OFF30 % promotion on HOCO chargers scheduled for tomorrow 08:00 isSCHEDULEDand becomesACTIVEat 08:00 without user action. - Creating a promotion with end before start returns
400 VALIDATION_FAILED. - Changing the value of an
ACTIVEpromotion is refused; extending its end succeeds and is journaled. - A guest opening RA-25 sees the conditions and eligible products with locks instead of prices.
Data — Promotion, PromotionScope, PromotionAudience, AuditLog.
Interfaces
- Screens: RA-24 Promotions & daily offers · RA-25 Promotion detail · BO-49 Promotions list · BO-50 Promotion editor · RW-10 Promotions & daily offers
- API:
promotions.listGET /promotions·promotions.getGET /promotions/{promotionId}·admin.promotions.crudGET · POST · PATCH · DELETE /admin/promotions[/{promotionId}]·admin.promotions.setStatusPOST /admin/promotions/{promotionId}/status
PRM-02 — Targeting by wilaya, segment, level, brand or volume
Statement — The system shall target each promotion at an audience defined by wilaya, segment, customer type, brands sold, level and volume conditions, and show and apply it only to resellers in that audience.
Rules
- Audience criteria (
PromotionAudience): wilayas (of the reseller's store), segments (CRM-06), customer types, brands the reseller sells, specific resellers, and — in V2 — loyalty levels. Values of one criterion are OR-ed, different criteria AND-ed; no criterion means every reseller. - Volume conditions: a minimum quantity of scoped items per line and/or a minimum scoped amount TTC in the cart; the promotion applies only when they are met, and the cart tells the reseller what is missing (« Encore 2 pièces pour profiter de l'offre »).
- Eligibility is evaluated by the server at every read with the reseller's current data; lists (
promotions.list,banners.list) and the catalog show only promotions the caller is eligible for. Guests see only promotions without audience restriction. - Delegates see the promotions of the reseller they prepare an assisted order for.
Acceptance criteria
- A promotion targeted at wilaya 31 and customer type
DETAILis visible to a Détail reseller of Oran and invisible to a Demi-gros reseller of Oran and to a Détail reseller of Alger. - With a minimum of 10 pieces per line, a line of 8 shows the normal price and the message, a line of 10 the promotional price.
- A guest does not see a promotion restricted to a segment.
Data — PromotionAudience, Reseller, ResellerSegment, CartItem.
Interfaces
- Screens: BO-50 Promotion editor
- API:
banners.listGET /banners·promotions.listGET /promotions·admin.promotions.crudGET · POST · PATCH · DELETE /admin/promotions[/{promotionId}]
PRM-03 — Stacking rules and priorities
Statement — The system should apply configurable stacking rules and priorities so that each cart line gets a predictable promotional price.
Rules
- Per line, the candidates are the active promotions covering the variant, targeted at the reseller, whose conditions are met.
- Non-stackable candidates: only one applies — the one giving the lowest unit price; ties go to the higher priority, then the earlier start.
- Stackable candidates apply after it, in descending priority, each on the running unit price;
FIXED_PRICEnever stacks. The unit price never goes below 0. FREE_DELIVERYis an order-level promotion and combines with line promotions.- The catalog shows the price the cart would apply under these rules; the promotion preview (
admin.promotions.preview) shows the result on a sample cart and reseller.
Acceptance criteria
- With a non-stackable -10 % and a non-stackable -500 DA on a 3 500 DA HT unit, the line gets 3 000 DA HT (the lower price).
- With a stackable -5 % of priority 1 on top of the -10 %, the unit price is 350 000 × 0.90 × 0.95 = 299 250 centimes, i.e. 2 992,50 DA HT (each step rounds half-up to the centime).
- A
FIXED_PRICEpromotion is never combined with another line promotion.
Data — Promotion (priority, stackable), CartItem.
Interfaces
- Screens: BO-50 Promotion editor
- API:
admin.promotions.crudGET · POST · PATCH · DELETE /admin/promotions[/{promotionId}]
PRM-04 — Dates, caps and targets respected; cart totals correct
Statement — The system shall apply a promotion only within its dates, within its quantity cap and to its audience, and the cart and order totals shall reflect it exactly.
Rules
- Active means status
ACTIVE, start ≤ now (server time) < end, and cap not reached; the engine re-checks the dates itself rather than trusting the status. - Cap: the maximum number of units sold under the promotion across all orders. Consumption happens at checkout (atomically, under lock) and at assisted-order confirmation; it is released when the order is refused or cancelled. When the cap is reached the promotion becomes
ENDED; a line asking more than the remaining cap gets the promotion on the remaining units only, priced per PRC-02. - A promotion that ends or is paused while it sits in a cart shows
PROMOTION_ENDEDon the line; at checkout the total changes and the answer is409 CART_CHANGED— the reseller never pays a price they did not see. - Order lines snapshot the promotion applied (id, label, type, value, discount); invoices show the discount per line.
admin.promotions.previewsimulates a promotion on a chosen reseller and cart before activation.
Acceptance criteria
- A promotion ending at 18:00: a cart read at 17:59 shows the promotional price, at 18:00
PROMOTION_ENDED, and a checkout at 18:01 with the old total returns409 CART_CHANGED. - With a cap of 100 units and 95 consumed, a line of 10 gets the promotion on 5 units; the next checkout ends the promotion.
- Cancelling an order that consumed 10 capped units gives them back to the cap.
- The order created from a cart with a -10 % line stores the promotion on the line and the invoice shows the discount.
Data — Promotion (cap, consumed), Order, OrderItem, CartItem, Invoice.
Interfaces
- Screens: RA-17 Cart · RA-25 Promotion detail · BO-50 Promotion editor
- API:
cart.getGET /cart·promotions.getGET /promotions/{promotionId}·admin.promotions.previewPOST /admin/promotions/preview
PRM-05 — Home banners and daily-offer carousel with countdown
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V1 | SHOULD | cahier §5, reference screens 1 and 7 | API · BO · RA · RW | SRV-19 · DSH-09 · WEB-02 · MRA-01 · MRA-06 · DES-10 |
Statement — The system should let the back-office publish banners and daily offers on the reseller home and promotions screens, with a countdown to the end of the offer.
Rules
- A
Bannerhas an image (BANNER_IMAGE, public CDN), a title, a placement (home carousel, top of the catalog), a target (a promotion, a product, a category or a brand — no external link), an audience (as PRM-02), start and end times, a position and a status; managed in BO-51 (promotions.*). - The home carousel (RA-09, RW-02) shows at most 5 active banners and daily offers for the caller, ordered by position, auto-advancing every 5 seconds; guests see untargeted banners.
- Daily offers show a countdown to their end (« Se termine dans 12 h 45 min 30 s », reference screen 7), computed from the server time sent with the response so a wrong phone clock does not change it; at zero the offer disappears.
- Tapping a banner opens its target; an ended or no-longer-visible target opens the promotions list instead.
Acceptance criteria
- Two daily offers ending tonight appear in the carousel with countdowns that match the server time even when the phone clock is 10 minutes off.
- A banner targeted at segment Grossistes Oran does not appear for other resellers nor for guests.
- A banner whose end time has passed disappears from
banners.list.
Data — Banner, Promotion, File.
Interfaces
- Screens: RA-09 Home · RA-24 Promotions & daily offers · BO-51 Banners & daily offers · RW-02 Home · RW-10 Promotions & daily offers
- API:
catalog.homeGET /catalog/home·banners.listGET /banners·admin.banners.crudGET · POST · PATCH · DELETE /admin/banners[/{bannerId}]
PRM-06 — Profitable packs (V2)
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V2 | SHOULD | cahier §4.6, §5.5, §14.2 | API · BO · RA · RW | SRV-31 · DSH-21 · WEB-20 · MRA-20 · DES-30 · SRV-29 |
Statement — The system should, in V2, offer packs — several references sold together at a pack price — added to the cart as one unit.
Rules
- A
Packhas items (variant and quantity), a pack price HT per customer type, dates, an audience and an optional cap; its availability is that of its scarcest item. cart.packs.addadds a pack line; stock is reserved and deducted per item; the pack price is spread over the items on the invoice in proportion to their normal prices.- Packs do not combine with promotions on their items; cancelling or returning releases or re-enters each item.
Acceptance criteria
- A pack Charge & Câble at 18 900 DA added to the cart appears as one line and reserves one charger and one cable at confirmation.
Data — V2 Pack, PackItem; CartItem, OrderItem.
Interfaces
- Screens: RA-37 Packs list & detail · BO-52 Packs · RW-15 Loyalty, rewards & packs
- API:
cart.packs.addPOST /cart/packs·packs.listGET /packs·packs.getGET /packs/{packId}·admin.packs.crudGET · POST · PATCH · DELETE /admin/packs[/{packId}]
Notes / open points — V2 (SRV-31); the V1/V2 split is D-16.
PRM-07 — Flash sales with countdown and the promotion tabs
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V1 | SHOULD | Client doc 03 (18 Oct 2026) | API · BO · RA · RW | SRV-39 · SRV-40 · DSH-14 · WEB-06 · MRA-11 · DES-23 |
Statement — The system should support flash sales — very short promotions with a visible countdown — and separate them from normal promotions in tabs (client document 03, SN-18 and SN-28; see smart notifications).
Rules
- A flash sale is a
PromotionwithisFlashSale = true; every rule of PRM-01 to PRM-04 applies (type, scope, audience, cap, stacking). Its durationendsAt − startsAtis between 2 h and 48 h (typical: 2 h, 6 h, 24 h, 48 h); otherwise400 VALIDATION_FAILEDwith field codeFLASH_SALE_DURATION. A flash sale is not a daily offer (both flags cannot be true). - Countdown: under 24 h left, « Plus que 05:12:45 » (hours, minutes, seconds); from 24 h, « Se termine dans 2 jours »; computed from
serverTimelike PRM-05; at zero the card shows « Offre terminée » and leaves the list at the next refresh. - RA-24 and RW-10 have tabs: « Promotions » (normal offers lasting several days or until the stock runs out, daily offers included), « Ventes flash » (
isFlashSale, sorted by end time) and, in V2, « Offres exclusives » (PRM-08). A tab without content is hidden; the screen opens on Ventes flash when one is active, else Promotions. promotions.list?tab=promotions|flash;catalog.homereturns up to 3flashSaleswithendsAtfor the home carousel.- Activation sends
flash_sale.startedto the audience (NOT-13).
Acceptance criteria
- Creating a flash sale of 72 h is refused with
FLASH_SALE_DURATION; one of 6 h is accepted. - A flash sale ending at 18:00 shows « Plus que 02:00:00 » at 16:00 server time even if the phone clock is 10 minutes off, and disappears from Ventes flash after 18:00.
- With no flash sale active, the Ventes flash tab is hidden.
Data — Promotion.isFlashSale.
Interfaces
- Screens: RA-24 · RA-09 · RW-10 · BO-50
- API:
promotions.list·catalog.home·admin.promotions.crud
PRM-08 — Exclusive offers by loyalty level (V2)
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V2 | COULD | Client doc 03 (18 Oct 2026) | API · BO · RA · RW | SRV-30 · MRA-20 · WEB-20 · DES-30 |
Statement — In V2, promotions targeted at loyalty levels (Gold, VIP…) could be shown in a third tab « Offres exclusives ».
Rules
- The audience criterion loyalty levels of PRM-02 (V2) marks a promotion as exclusive; the tab shows the level badge (« Réservé Gold »).
- Resellers below the level do not see the offer (no teaser in V2 unless HOCO asks).
Acceptance criteria
- A promotion for Gold and VIP appears in Offres exclusives for a Gold reseller and nowhere for a Silver one.
Data — PromotionAudience (levels), V2 LoyaltyAccount.
Interfaces — V2 RA-24 / RW-10 tab.
Error codes
| Code | HTTP | Raised when |
|---|---|---|
PROMOTION_TRANSITION_NOT_ALLOWED | 409 | A promotion status move outside its lifecycle |
CART_CHANGED | 409 | A promotion ended or its cap ran out between display and checkout |
PERMISSION_DENIED | 403 | Status change without promotions.validate; below-cost confirmation without pricing.view |
| Issue code | — | PROMOTION_ENDED (cart warning) |
Field codes in 400 VALIDATION_FAILED | 400 | End before start, value out of bounds, empty scope, FLASH_SALE_DURATION (flash sale outside 2 h–48 h, or also a daily offer) |
Invoicing, payments & credit
Invoices with legal mentions and statuses, payment methods, payments recorded and declared, allocation, credit control at checkout, credit notes and receipts, the reseller finance view, and the V2 detailed credit, reminders and accounting exports.
Loyalty programme
Requirements LOY-01 to LOY-07 (V2): points earning, expiry and reversal, Bronze to VIP levels, rewards and redemption, the traceable points history, missions and challenges, and the Top 3 resellers of the month.