Kortingen & coupons¶
Kortingen lopen in deze shop via een couponsysteem. Een coupon is een code die de
klant in de winkelwagen invoert; bij een geldige code wordt een beloning (reward)
toegepast. Het systeem staat volledig onder src/Atraxion/Coupon/ (moderne
Doctrine/DI-laag) en kent vier beloningstypes: een bedrag- of percentage-korting, een
gratis artikel, gratis montage en loyaliteitspunten.
Datamodel¶
De centrale entiteit is Coupon (src/Atraxion/Coupon/Entity/Coupon.php), met de
code (varchar) als primaire sleutel. Een coupon heeft één beloning. De beloningen
zijn gemodelleerd met Single Table Inheritance in de tabel coupon_rewards; de
discriminator staat in src/Atraxion/Coupon/Resources/doctrine/Reward.AbstractReward.orm.xml:7-12:
| Discriminator | Klasse | Betekenis |
|---|---|---|
discount |
Entity/Reward/Discount.php |
Vast bedrag of percentage korting op het winkelwagentotaal |
gift |
Entity/Reward/Gift.php |
Gratis artikel toegevoegd aan de winkelwagen |
free_assembly |
Entity/Reward/FreeAssembly.php |
Gratis montage op één set wielen (montageprijs op 0) |
loyalty |
Entity/Reward/LoyaltyPoints.php |
Kent X-Coins / loyaliteitspunten toe |
De korting kent twee subtypes (Entity/Reward/Discount.php:7-8): TYPE_FIXED
(fixed) en TYPE_PERCENTAGE (percentage), met een amount-veld. Gift heeft een
artikel-FK, een quantity en een vlag trackStock; LoyaltyPoints een
puntenaantal. Beloningen hebben meertalige omschrijvingen via coupon_reward_translations
(enkelvoud/meervoud per taal, Entity/Reward/RewardTranslation.php).
De belangrijkste tabellen:
| Tabel | Inhoud |
|---|---|
coupons |
de coupon zelf (code, geldigheid, limieten, FK naar beloning) |
coupon_rewards |
de beloning (STI) |
coupon_reward_translations |
meertalige beloningsteksten |
coupon_used |
gebruikslog: code + klant + order (Entity/CouponSpent.php) |
Op de orders-tabel staan daarnaast discount (het toegepaste bedrag) en CouponId
(FK naar coupons.code, ON DELETE SET NULL).
De coupon coupons bevat onder meer: startsAt/endsAt, active,
limitedToOwnedStock, usesPerCustomer (NULL = onbeperkt), minimumOrderAmount,
vehicleCategories (simple_array) en audit-velden createdBy/createdAt/updatedAt.
Validatie bij de checkout¶
De code wordt toegepast via CartController::applyCouponIfPresent()
(_core/controller/classes/atx/customers/cart/CartController.php:484). De code komt uit
de POST-parameter coupon_code of uit $_SESSION['coupon_code']
(CartController.php:486). De flow:
flowchart TD
A[coupon_code uit POST of sessie] --> B[CouponRepository::findByCode]
B --> C[CouponService::validate → CouponValidator]
C -->|fouten| X[code uit sessie verwijderen, melding tonen]
C -->|geldig| D[CouponService::apply → RewardHandler]
D --> E[beloning op de winkelwagen toegepast]
CouponService (src/Atraxion/Coupon/CouponService.php) biedt validate(),
isValid(), apply() en undo(). De CouponValidator
(src/Atraxion/Coupon/Validator/CouponValidator.php) controleert onder meer:
isActive()en of de huidige datum binnenstartsAt/endsAtvalt (CouponValidator.php:26);minimumOrderAmount≤ volledig winkelwagentotaal (artikelen + transportkosten + recyclingkosten + betaalkosten − korting, viaCart::getTotalCost(),CouponValidator.php:31–32);limitedToOwnedStock→ de winkelwagen moet minstens één Atraxion-eigen artikel bevatten;- merk-, artikel-, banden-/velg-/accessoiretype-, voertuigcategorie-, klant-tag-, specifieke-klant- en klantcategorie-restricties;
usesPerCustomer(geteld uitcoupon_used);- voor
gift: Atraxion-voorraad > 0; - voor
loyalty: de klant moet zijn opgemeld voor het loyaliteitsprogramma; is dat niet zo, dan wordt hij automatisch opgemeld viacustomerLoyaltyService->optIn()(CouponValidator.php:115-117).
Beloningen toepassen¶
CouponService::apply() delegeert naar de juiste handler
(src/Atraxion/Coupon/Handler/):
- Korting (
DiscountHandler): bij een vast bedrag wordtCart::setDiscount(amount)gezet (mits kleiner dan het totaal); bij een percentagetotalArticles × amount / 100.OrderBuilder::addDiscount()kopieertcart->getDiscount()naar de order en trekt het van het ordertotaal af. - Gratis artikel (
GiftHandler): maakt eenCouponGiftaan viaCart::setCouponGift(). Het cadeau-artikel wordt nooit als orderregel aangemaakt. Bij het aanmaken van de order wordt uitsluitend de couponcode ('Code ' . $coupon->getCode()) als interne notitie aan de order toegevoegd (CartController.php:1613). Het cadeau is dus niet zichtbaar als orderregel in de order of ERP-export — alleen de code-notitie verwijst ernaar. - Gratis montage (
FreeAssemblyHandler): itereert de voertuigsets, stopt na de eerste (break) en zetsetAssemblyUnitPrice(0)op zowelgetFrontWheelSet()alsgetRearWheelSet()van die set — dus op tweeWheelSetLineItems (voor- én achteras), niet slechts één (FreeAssemblyHandler.php:31–47). - Loyaliteitspunten (
LoyaltyPointsHandler): zetCart::setLoyaltyPoints(amount); bij het opslaan van de order worden deze als broncouponaan de loyaliteitspunten toegevoegd in statuspending(LoyaltyPoints.php:24). Ze worden op dat moment niet onmiddellijk uitbetaald:payoutByOrder()verwerkt alleenconfirmed-punten, maar de coupon-punten zijn op dat ogenblik nogpending. Ze volgen de normale toestandsmachine:pending→confirmed(na betaling viaCheckPaymentStatus.php:117) →paid(via de dagelijkse cronatx:loyalty:payout-confirmed-points).
Bij het persisteren van de order (CartController.php:1647) wordt het gebruik
geregistreerd via $coupon->addUse($newOrder) in coupon_used.
Scoping¶
Er is geen per-channel-scoping: coupons gelden over alle channels heen. Alle
beperkingen lopen via optionele join-tabellen op coupons:
| Beperking | Join-tabel | Sleutel |
|---|---|---|
| Merken | coupon_limits_brands |
BrandId |
| Klant-tags | coupon_limits_tags |
tag_id |
| Specifieke klanten | coupon_limits_customers |
Klantnr |
| Klantcategorieën | coupon_limit_customer_categories |
Klantcatcode |
| Bandtypes | coupon_limit_tyre_types |
TypeId |
| Velgtypes | coupon_limit_wheel_types |
Typenr |
| Specifieke artikelen | coupon_limit_articles |
Artnr |
| Voertuigcategorieën | kolom coupons.vehicleCategories (simple_array) |
— |
De scoping is dus per merk / artikel / type / voertuigcategorie en per klant / klant-tag / klantcategorie — niet per verkoopkanaal.
Beheer in de backoffice¶
Coupons worden beheerd in de backoffice via
_admin/controller/classes/atx/AdminCouponsController.php en de bijbehorende view
_admin/view/classes/atx/AdminCouponsView.php. Functies: een filterbare lijst,
inline aan/uit zetten per coupon, een aanmaak-/bewerkformulier met alle
scoping-opties, en een XLSX-export van alle coupongegevens.
Zie ook¶
- Winkelwagen & checkout — waar de couponcode wordt ingevoerd.
- Loyaliteit — de
loyalty-beloning. - Bestellingen & orderverwerking —
discount/CouponIdop de order.