Where the Checkout survey stopped — the instant Order exists — this
one starts. A feature-by-feature pass across Shopify, WooCommerce, PrestaShop, and
(briefly) Magento's post-placement order layer — sourced, not recalled from memory —
checked against what Lunar's Order model and the
already-shipped Filament ManageOrder page actually support
today. For deciding what the new Order module needs to own, not a
build order.
One field, or several axes — and who's allowed to move it.
ManageOrder::paymentStatus() derives a real value from transactions()/captureTotal()/refundTotal()/intentTotal() — but it's a computed display value on the admin page, not a stored column or something the rest of the system (mailers, automations) can key off. Shopify and Magento both make payment status a first-class, independently-queryable dimension; here it's derived on the fly, once, in one Filament page.paymentStatus() exists for shipment/fulfillment state — Order has no shipments() relation of its own at all; it's added dynamically by Modules\Core\Shipping\Providers\ShippingServiceProvider::resolveRelationUsing(), outside Order's own boundary (see docs/checkout.md, "Where Order would likely absorb work"). Every platform researched (Shopify, Woo, PrestaShop, Magento) treats "has this shipped" as derivable from child records, not a manually-set field — Lunar has the child records (Shipment) but no derived status method reading them.ManageOrder ships a working UpdateStatusAction out of the box, backed by config('lunar.orders.statuses') — a flat, merchant-configured list, each entry carrying a label/color/favourite flag. Closer to WooCommerce's single linear field than Shopify's multi-axis split.config('lunar.orders.statuses') already declares mailers and notifications arrays — the PrestaShop-style shape is there in config — but per the Checkout survey's finding, nothing in core actually reads and dispatches from those keys on a transition. The data model for "status carries behavior" exists; the behavior doesn't.OrderStatusUpdated/equivalent exists anywhere in core. UpdateStatusAction just writes the column. Anything wanting to react to a status change — a confirmation email, a webhook, re-deriving payment/fulfillment status — has to hook the raw Eloquent Order::updated() event and diff status itself.Turning a placed order into a package that moves.
Modules\Core\Shipping\Models\Shipment (carrier, tracking reference, label-printed timestamp, manifest reference) already exists and belongs to Order. Built this session, ahead of most gaps in this survey — the record shape is closer to Magento's per-shipment entity than Woo's "no shipment entity at all."Shipment has no quantity-per-line or order_line_id concept — it's one shipment record per carrier voucher, with a parent_reference for ACS's own multipart-voucher case (one physical order split into multiple packages by the carrier), not a per-line-item fulfillment split decided by staff. Closer to "multiple packages for one shipment" than Magento's true per-line partial-shipment model.Modules\Core\Shipping\Extensions\OrderViewExtension adds a working "Create Shipment" header action to ManageOrder, resolving a CarrierFulfillmentInterface by the order's chosen shipping method and calling createShipment() — genuinely wired, not a stub. Currently lives under Shipping, flagged in docs/checkout.md as conceptually an Order concern.Shipment.tracking_reference/carrier exist and are populated by createShipment(); PollShipmentTrackingJob (scheduled every 30 minutes) keeps ShipmentInfo checkpoints current via CarrierFulfillmentInterface::trackShipment(). Genuinely ahead of PrestaShop's thin order_carrier.tracking_number field — this has a real checkpoint history, not just one string.ShipmentInfo, TrackingStatus enum including Delivered) but nothing writes them back onto Order.status — a delivered shipment doesn't move the order out of whatever status it was already in. Every platform researched treats "delivered" as a status a customer/staff can see on the order, not something buried one relation away.PollShipmentTrackingJob's own status updates either.Money moving back out, and orders that never should have been placed.
ManageOrder's refund action already exists — picks a transaction, an amount (validated against availableToRefund()), and notes, then calls the driver's own Transaction::refund(). This is genuinely native, matching Woo/Magento's line-item-adjacent (if not line-item-exact) refund UX.ManageOrder's capture action + requiresCapture()/canBeRefunded() guard methods already exist, delegating to Transaction::capture() — this is the Stripe "authorize now, capture later" flow's admin-side half, already built ahead of most gaps here.ManageOrder — a cancellation today would just be picking a "cancelled"-labeled entry from the generic status dropdown (01), with no automatic refund trigger, no stock-release logic, and no distinction from any other manual status edit.paymentStatus() recomputes correctly from transactions when the admin page loads, but a refund doesn't push the order into a refunded/partially-refunded overall status the way Shopify's displayFinancialStatus does automatically.The one area every researched platform treats as optional, not core.
Return/RMA model, status set, or request flow exists anywhere in this codebase. Consistent with the research: Shopify is the only platform of the four with this genuinely native; PrestaShop ships it off-by-default; Magento gates it behind the paid Adobe Commerce tier; WooCommerce lacks it entirely. Safe to treat as a real gap, not an urgent one.CarrierFulfillmentInterface already has the label-printing primitive (printLabel()) a return label would reuse, so the carrier-side plumbing isn't the blocker, the RMA request/approval model is.Changing a placed order — and where every platform draws the line.
Shipment existing, per the pattern all four researched platforms converge on.OrderAddress rows are snapshotted at creation (see Checkout survey, 02) and nothing in ManageOrder exposes editing them afterward — every platform researched treats address edits as lower-risk than line-item edits and allows them more freely; this codebase currently allows neither.ManageOrder's edit_tags action already works — the one piece of native post-placement editing that exists today, via HasTags on the Order model.The one thing every researched platform treats as non-negotiable.
Order already uses Spatie's LogsActivity trait — every save is recorded with a diff, same underlying mechanism already relied on elsewhere in this codebase (staff activity log, translation history). Structurally equivalent to PrestaShop's order_history table, just via a different package.