Ga naar inhoud

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.
  • Klantnummercustomer.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

  1. Retourregels: findItemByOrderLineId() per item; stel hoeveelheid en reden in.
  2. Reden "damaged on delivery" wordt server-side verwijderd als de factuurleeftijd > 3 dagen is.
  3. Validatie via ObjectValidator — verplichte velden: businessName, contactName, contactEmail, alle vier adresvelden, minimaal één item, agreeTerms.
  4. Geüploade foto's: verplaatst naar /images/return-requests/{orderId}/, verkleind naar maximaal 1000px breedte, relatieve paden opgeslagen als JSON-array in $return->images.
  5. EntityManager::persist($return) + flush().
  6. ReturnRequestMail verstuurd naar retour@atraxion.com (uitgeschakeld in dev-omgeving).
  7. 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.