the.rampage.rado Posted 2 hours ago Posted 2 hours ago Hi there. I would like to start a discussion on the RMA part of thirty bees. How do you use it? Do you find it easy to use? Do your customers find it easy to navigate? What modules do you have that are using it? What would you improve in it? Do you use it only for warranties/claims/etc. or also for product exchanges, etc? What is your typical use case/flow? ------------ I'm currently not using it as I find it very limited and hard to use for me and for my customers. I handle all (very few) quality/warranty claims via email/phone and I want to start using the RMA for this. BUT, with the slow growth of my shops I started experiencing a rise in exchange requests for items that are the wrong size. The customer wishes to return the initially purchased item and receive a different size. For now, they have to call/email me to arrange this. I would like to centralize this flow in one place. Preferably with all other returns, different from the cool-off period returns (which are almost non-existent for me for now). I have the idea to use the current RMA for this. Here is what the LLM spit out as an idea, and I quite like it. I would like to see the RMA as a one-stop-shop for all warranty and exchange flows in the shop. In the future, we can even implement some connection to the Withdrawal module so we can truly have everything in one place. I would like for the customer to have the opportunity to file a claim in their profile, so we can handle every request in a specific flow. I would call this an evolution and a step forward for the RMA system that will truly make it useful for me (and hopefully others). Will you use something similar if you currently don't use the RMA as well, and what ideas do you have that you think will be useful in your work day? Quote The current thirty bees RMA model is quite minimal: OrderReturn stores the order, customer, one numeric state, a free-text question and dates; order_return_detail essentially identifies the original order line/customization and quantity. Return states are also basically just configurable names/colors with an active flag. That is a good base for backwards compatibility, but not enough for a modern after-sales workflow. The model I would aim for Think of an RMA as an after-sales case: Order → RMA case → RMA items → desired resolution → inbound shipment → inspection → resolution → outbound/refund For each returned item, the customer should choose two separate things: Concept Examples Reason Too small, too large, wrong item, defective, damaged, changed mind Requested resolution Exchange, refund, repair, store credit Exchange target Same product → size XL Quantity 1 Comment Optional Photos Optional/required depending on reason So: “Size L is too small; exchange it for XL” becomes structured data instead of something written into question. That distinction will make the system dramatically more useful later. Backwards compatibility I would not replace order_return or order_return_detail. Existing modules and themes potentially depend on them, and thirty bees itself reads returned quantities from order_return_detail. Instead: Existing / legacy ----------------- tb_order_return tb_order_return_detail tb_order_return_state tb_order_return_state_lang New --- tb_order_return_reason tb_order_return_reason_lang tb_order_return_resolution tb_order_return_history tb_order_return_attachment tb_order_return_shipment tb_order_return_reservation tb_order_return_order The old tables remain valid and continue to work. Existing code asking: $orderReturn->state still gets a valid state. Existing code reading: order_return_detail still sees the products/quantities involved. The new tables simply enrich the RMA. That is the safest BC architecture. How I would implement it Modernize the RMA concept without breaking the legacy API. Keep OrderReturn as the RMA header. Keep OrderReturn::addReturnDetail() and existing queries working. Add services such as OrderReturnService, OrderReturnResolutionService, OrderReturnStockService, OrderReturnNotificationService and OrderReturnEligibilityService. Controllers should progressively become thin orchestration layers. The current FO still uses the traditional order-return controller/template architecture, so we can modernize internally while retaining the existing URLs and theme contract. Introduce structured reason codes. Instead of relying on the free-text question, create configurable reasons such as too_small, too_large, wrong_product, damaged, defective, not_as_expected, withdrawal, and other. Reasons should be multilingual and optionally enable/disable specific resolutions. For example, too_small can allow exchange/refund, while defective could allow replacement/refund/repair. The legacy question remains and becomes the customer's optional comment. Introduce resolutions per returned line. A single RMA may contain two products with different outcomes. One might be refunded while another is exchanged. I would therefore not put resolution_type directly on order_return. Create something along the lines of: tb_order_return_resolution id_order_return_resolution id_order_return id_order_detail id_customization quantity reason_code resolution_type id_product_target id_product_attribute_target price_original price_target price_difference status date_add date_upd For a size exchange: original: Product 123 Combination 456 = Size M resolution: exchange target: Product 123 Combination 458 = Size L This also lets us support colour exchanges or even replacement with another product later. Create a proper RMA state machine. Keep order_return.state because of BC, but add meaning and history behind it. I would use operational states approximately like: Requested ↓ Awaiting approval ↓ Approved / Awaiting return ↓ In transit ↓ Received ↓ Inspection ↓ Approved for resolution ↓ Refund pending / Exchange pending ↓ Replacement shipped / Refunded ↓ Completed There should also be: Rejected Cancelled by customer Expired The important change is tb_order_return_history. Currently the RMA essentially has one current state; we should know who changed it, when, and why. For example: 22 Sep 10:14 Customer Requested exchange M → L 22 Sep 10:21 Employee #4 Approved 23 Sep 16:42 System Return shipment collected 25 Sep 09:10 Employee #3 Item received 25 Sep 09:12 Employee #3 Condition accepted 25 Sep 09:13 System Size L allocated 25 Sep 15:03 Employee #3 Replacement dispatched order_return.state simply remains the legacy projection of the current state. Make exchanges combination-aware. This is particularly important for your shops because sizes are combinations. When the customer selects Exchange, thirty bees should retrieve the combinations belonging to that product and show only valid alternatives. For example: Exchange for: ○ 39 ○ 40 ● 41 ○ 42 — unavailable ○ 43 There should be configuration controlling whether unavailable combinations are hidden or shown as unavailable. For a simple size exchange, don't let the customer arbitrarily search the catalogue. Start with: Same product → different combination Later we can add: Exchange for another product as a separate feature. Add stock reservation specifically for exchanges. This is one of the most important parts. Imagine the customer requests XL, returns L five days later, and XL sells out in between. We should therefore support an optional reservation: Requested exchange: M → XL XL available: 2 Reserve: 1 × XL I would make the policy configurable: Reserve replacement: ○ Never ○ When RMA is approved ○ Immediately when customer requests exchange My default would be reserve on RMA approval. The reservation should have an expiry and be released automatically if the return is cancelled/rejected/expires. Importantly, a reservation isn't the same thing as decrementing physical stock. Handle the inbound and outbound legs separately. A return/exchange actually has two possible shipments: Customer → shop and, for an exchange: Shop → customer So don't put one tracking number directly onto the RMA. Use: tb_order_return_shipment direction = inbound/outbound carrier tracking_number label/reference shipping_cost status date_shipped date_delivered This would eventually integrate very nicely with tbspeedy. For example your BO might show: RETURN Inbound Speedy Shipment: 123456789 Delivered 25 Sep Replacement Speedy Shipment: 987654321 Dispatched 25 Sep The exchange workflow I'd build The customer's Order Details page could become: ASICS Gel-Rocket Size: 42 Qty: 1 [ Return / exchange item ] Then: What would you like to do? ● Exchange ○ Return for refund ○ Report a problem Exchange: Why? ● Too small ○ Too large ○ Prefer another colour ○ Other Then: Choose replacement Size 40 Size 41 Size 42 ← purchased Size 43 Size 44 Size 45 Then a summary: YOU ARE RETURNING ASICS Gel-Rocket Size 42 × 1 YOU WILL RECEIVE ASICS Gel-Rocket Size 43 × 1 Price difference: €0.00 Customer clicks: Request exchange That creates the normal legacy RMA plus the new exchange information. Same-price versus different-price exchanges I would deliberately treat these differently. Same price This is the easy and common case: M → L €39.90 → €39.90 Difference: €0 No need to alter the original order. The RMA handles: return M + stock M -stock L ship L The original commercial order remains untouched. Replacement costs more Example: Original: €39.90 Replacement: €44.90 Customer owes: €5.00 Don't rewrite the old order. The RMA should create a linked payment/new-order operation: RMA #123 Original order #5000 Exchange difference: +€5 Related order/payment #5019 Replacement costs less Original: €44.90 Replacement: €39.90 Refund due: €5 Use the existing refund/credit-slip machinery where possible. This is much safer than altering historic order_detail records. Do not convert the original order into the new size This deserves emphasis. If somebody bought: Order #1000 Shoe / size 42 and exchanges for 43, do not change the original OrderDetail to size 43. Order #1000 must forever show what was actually purchased: Purchased: size 42 The RMA should show: Returned: size 42 Replacement: size 43 That preserves invoices, statistics, payment reconciliation, audit history and module assumptions. Back Office redesign Rather than the old mostly tabular Merchandise Returns screen, I'd turn it into an operational RMA workspace. Header: RMA #000184 ORDER #001284 CUSTOMER Created Age Current status Main section: RETURNED ITEM [image] hmlCORE XK POLY JERSEY Size: M Qty: 1 Reason: Too small Requested resolution: Exchange Replacement: hmlCORE XK POLY JERSEY Size: L Stock: 7 available 1 reserved Then actions: Approve Reject Mark received Inspect Accept item Reject item Prepare exchange Issue refund Create credit slip Generate return label Generate replacement label And below: TIMELINE with all history and communication. This would be much more useful for actual daily operations. Customer communication We should stop making every RMA state dependent on manually written emails. Introduce events/templates: rma_created rma_approved rma_rejected rma_waiting_return rma_received rma_exchange_reserved rma_exchange_unavailable rma_exchange_shipped rma_refunded rma_completed Modules could hook into these events. For example: Hook::exec('actionOrderReturnStatusChanged', ...); Hook::exec('actionOrderReturnCreated', ...); Hook::exec('actionOrderReturnReceived', ...); Hook::exec('actionOrderReturnResolutionCreated', ...); Hook::exec('actionOrderReturnExchangeShipped', ...); We should retain existing hooks/behaviour and add new hooks, never silently change the meaning of old ones. Eligibility should also become a service The current front controller exposes the configured return-day value to the template via PS_ORDER_RETURN_NB_DAYS. Longer term, eligibility shouldn't be scattered through controllers/templates. Something like: $result = $returnEligibilityService->check( $order, $orderDetail, $customer ); could return: ReturnEligibilityResult { eligible: true, refundAllowed: true, exchangeAllowed: true, deadline: ..., reasons: [...], } This gives us a future path for: Different return periods Category exclusions Customised products Clearance items B2B customers Defective-product claims Warranty workflow Different countries Different shops without adding more conditionals everywhere. RMA vs warranty I would also future-proof the naming internally. Not every after-sales case is really a merchandise return. Eventually: AfterSalesCase ├── Withdrawal / Return ├── Exchange ├── Warranty claim ├── Wrong shipment └── Damaged shipment But I would not rename OrderReturn now because that would cause unnecessary compatibility problems. Externally we can call the BO section: Returns & Exchanges while internally maintaining OrderReturn. Attachments Another worthwhile addition: Customer attachment Employee attachment Courier document Product-condition photo For damaged or defective, BO configuration could say: Require photograph: YES For: Too small Too large Changed mind no photo would be needed. Configuration I would add a dedicated: Orders → Returns & Exchanges → Settings with settings such as: Setting Example Returns enabled Yes Exchanges enabled Yes Return period 14 days Exchange reservation On approval Reservation lifetime 7 days Allow exchange to unavailable size No Allow exchange to another product No Customer pays return transport configurable Merchant pays replacement transport configurable Require approval before shipment Yes Photos required for damage Yes Multistore scope should work from the beginning. API / module architecture The services should be callable independently from the controllers: OrderReturnService OrderReturnEligibilityService OrderReturnReasonService OrderReturnResolutionService OrderReturnExchangeService OrderReturnReservationService OrderReturnInventoryService OrderReturnShipmentService OrderReturnRefundService OrderReturnNotificationService OrderReturnHistoryService That will make it much easier later to expose: Webservice/API mobile interface courier integration ERP integration warehouse integration without copying controller logic.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now