Retours & RMA¶
Professionele klanten kunnen via de webshop een retourverzoek indienen voor een bestelling. Het proces bestaat uit vier stappen, afgehandeld door één controller. Er is geen apart admin-portaal voor retourverwerking: het ingediende verzoek genereert een e-mail naar het Atraxion-magazijn.
Proces-overzicht¶
flowchart TD
A[Klant zoekt factuur\n/retour/zoek] --> B{Factuur + order gevonden\nen retourneerbare items?}
B -- Nee --> C[/retour/geweigerd\nredencode 0–2]
B -- Ja --> D[Geschiktheidscontroles\n/retour]
D --> E{Poort 3: Klant is eigenaar?}
E -- Nee --> C2[/retour/geweigerd\nredencode 3]
E -- Ja --> F{Poort 4: Al retour ingediend?}
F -- Ja --> C3[/retour/geweigerd\nredencode 4]
F -- Nee --> G[Retourformulier invullen\nItems, reden, adres, foto's]
G --> H[Indienen]
H --> I[order_return record opslaan\nMail naar retour@atraxion.com]
I --> J[/retour/ontvangen\nBevestigingsscherm]
Controller: _core/controller/classes/atx/orders/ReturnController.php
View: _core/view/classes/atx/orders/ReturnView.php
Routes (_core/config/pages.xml:663–681):
| Paginanaam | URL (nl) | Action |
|---|---|---|
return-find |
/nl/retour/zoek |
findPage() |
return |
/nl/retour |
returnPage() |
return-denied |
/nl/retour/geweigerd |
returnDeniedPage() |
return-received |
/nl/retour/ontvangen |
returnReceivedPage() |
Stap 1 — Factuur opzoeken (findPage())¶
De klant voert drie velden in:
- Factuurjaarprefix — dropdown beperkt tot huidig en vorig jaar (formaat
VKF/{jj}/). - Factuurnummer — numeriek.
- Klantnummer —
customer.Klantnr.
Bij POST:
1. Factuur opzoeken via InvoiceManagerInterface::findOneBy(['number' => …, 'customer' => …]).
2. Bijbehorende order vinden via OrderManagerInterface::findOneBy(['reference' => invoice->getReference()]).
3. Als de order bestaat en order->hasReturnableItems() waar is → redirect naar return met id- en ref-parameters.
4. Anders: foutmelding "Order kon niet worden gevonden."
Stap 2 — Geschiktheidscontroles (returnPage())¶
Vijf poorten worden achtereenvolgens gecontroleerd. Bij mislukking volgt redirect naar return-denied met een redencode:
| Redencode | Conditie |
|---|---|
0 |
Ontbrekende id- of ref-parameter |
1 |
Order niet gevonden of referentie komt niet overeen |
2 |
order->hasReturnableItems() is onwaar (geen factuur, of factuur ouder dan 14 dagen) |
3 |
Ingelogde klant is niet de ordereigenaar (en geen Atraxion-medewerker) |
4 |
Er bestaat al een ReturnRequest voor deze order |
Als alle poorten passeren: een nieuw ReturnRequest($order)-object wordt aangemaakt (constructor filtert automatisch op retourneerbare items), contact- en adresvelden worden vooringevuld vanuit de ingelogde klant, en het formulier wordt getoond.
Stap 3 — Formulier invullen en indienen (POST in returnPage())¶
De klant vult in:
- Bedrijfsgegevens:
businessName,contactName,contactEmail,contactPhone. - Ophaaladres (
pickUpAddress): straat, postcode, stad, land. - Opmerking (
comment). - Retourregels (
items[]): per geselecteerde orderregel de hoeveelheid en reden. - Foto's (
images[]): maximaal meerdere bestanden. - Akkoord met voorwaarden (
agreeTerms): verplicht.
Serverside verwerking¶
- Retourregels:
findItemByOrderLineId()per item; stel hoeveelheid en reden in. - Reden "damaged on delivery" wordt server-side verwijderd als de factuurleeftijd > 3 dagen is.
- Validatie via
ObjectValidator— verplichte velden:businessName,contactName,contactEmail, alle vier adresvelden, minimaal één item,agreeTerms. - Geüploade foto's: verplaatst naar
/images/return-requests/{orderId}/, verkleind naar maximaal 1000px breedte, relatieve paden opgeslagen als JSON-array in$return->images. EntityManager::persist($return)+flush().ReturnRequestMailverstuurd naarretour@atraxion.com(uitgeschakeld in dev-omgeving).- Redirect naar
return-received.
Stap 4 — Bevestiging en weigering¶
return-received: enkel een bevestigingsscherm; geen verdere logica.return-denied: toont een redenspecifieke boodschap op basis van CMS-tekstsleutels (order_not_found,return_not_eligible,order_not_yours,return_exists).
Zakelijke regels¶
| Regel | Detail |
|---|---|
| 14-dagen venster | Order::hasReturnableItems() (_core/model/classes/atx/orders/Order.php:1248) vergelijkt de factuurdatum met vandaag. Bij meer dan 14 dagen → niet retourneerbaar. |
| Alleen fysieke producten | OrderArticle::canReturn() (_core/model/classes/atx/orders/OrderArticle.php:457) delegeert naar isPhysicalProduct(). Diensten/digitale artikelen zijn uitgesloten. |
| Één retour per order | Poort 4 blokkeert een tweede ReturnRequest voor dezelfde order. |
| Beschadigd bij levering | Reden alleen beschikbaar als de factuur ≤ 3 dagen oud is; server-side afgedwongen. |
Datamodel¶
Tabel order_return (entiteit: atx\orders\ReturnRequest)¶
ORM-mapping: _core/config/doctrine/orders.ReturnRequest.orm.xml
| Kolom | Type | Toelichting |
|---|---|---|
id |
MEDIUMINT UNSIGNED AUTO_INCREMENT | PK |
order_id |
MEDIUMINT UNSIGNED NOT NULL | FK → orders.OrderId (één-op-één, owning side) |
business_id |
MEDIUMINT UNSIGNED nullable | FK → customer.Klantnr (zakelijke klantrekening, niet customer_user) |
business_name |
VARCHAR | Bedrijfsnaam aanvrager |
contact_name |
VARCHAR | Contactpersoon |
contact_email |
VARCHAR | E-mailadres |
contact_phone |
VARCHAR | Telefoonnummer |
comment |
VARCHAR(1024) | Vrije opmerking |
images |
JSON | Array met relatieve bestandspaden |
street, postcode, city, country |
VARCHAR | Ophaaladres (embedded Address-value-object) |
Tabel order_return_item (entiteit: atx\orders\ReturnRequestItem)¶
ORM-mapping: _core/config/doctrine/orders.ReturnRequestItem.orm.xml
| Kolom | Type | Toelichting |
|---|---|---|
id |
UNSIGNED AUTO_INCREMENT | PK |
return_id |
FK | → order_return.id |
item |
MEDIUMINT UNSIGNED NOT NULL | FK → order_article.OrderArticleId |
quantity |
SMALLINT UNSIGNED | Terug te sturen hoeveelheid |
reason |
VARCHAR | Redencode (vrije tekst uit dropdown) |
De koppeling met orders is één-op-één: Order is de owning side (heeft <join-column name="return_id"/> in _core/config/doctrine/orders.Order.orm.xml:199–204). ReturnRequest heeft op zijn beurt ook een <join-column name="order_id"/> (owning side richting orders). Er is geen mapped-by-attribuut, dus dit is geen klassieke Doctrine bidirectionele relatie.
Wat er na indiening gebeurt¶
Het systeem slaat het verzoek op en verzendt een e-mail naar het magazijn. Verdere verwerking (goedkeuring, terugboeking, creditnota) verloopt buiten het systeem. Er is geen statusbeheerscherm in de admin voor retourverzoeken.
Kruislinks¶
- Bestellingen & orderverwerking — orderstatus, orderdocumenten
- Notificaties & e-mails —
ReturnRequestMailen het e-mailtemplate-systeem - Admin & backoffice — Atraxion-medewerkers kunnen retourverzoeken van een andere klant bekijken (poortscheck slaat over voor staff)