Skip to content

Shipping in a marketplace

A marketplace order can contain products from several vendors, and each vendor dispatches its own goods. That single fact drives the whole shipping model.

One shipment per vendor, always

The plugin replaces Sylius' default shipment processor with one that groups the order's shippable units by vendor and produces one Shipment per vendor present in the order. This is an invariant, not an option: it is what lets each vendor ship, track and confirm only its own part of the order, and it holds in every installation regardless of configuration.

The vendor of each unit is resolved through the vendor resolver, the same extension point used to split orders and calculate commission — so if you change how items map to vendors, shipments follow automatically.

Each vendor order also carries its own shippingState (graph odiseo_marketplace_vendor_order_shipping, initial place ready):

StateMeaning
readyNothing shipped yet.
partially_shippedSome of the vendor's shipments have gone out.
shippedAll of them have.
cancelledThe vendor's slice was cancelled.

It is derived from the underlying Sylius shipments, not set by hand.

Who owns the shipping methods

What is configurable is who defines the shipping methods and who collects the shipping money: shipping.method_owner, either vendor (the default) or operator.

vendor — each vendor sets its own rates

Vendors create their own shipping methods in the seller panel and manage their own shipments. Every shipment gets its own shipping charge, calculated from that vendor's method, and the resulting adjustment is tagged with the vendor. A buyer ordering from three vendors is charged three times.

Shipping income belongs to the vendor that earned it.

operator — the marketplace sets the rates

The operator defines vendor-less shipping methods centrally. The order still splits into one shipment per vendor (the invariant above is untouched), but the buyer is charged once: the first shipment carries the calculated charge and every other shipment gets a zero-amount adjustment of the same type — so anything reading a shipment's shipping adjustment still finds one, and no code needs a special case for "this shipment has no charge".

Because the methods have no vendor, those adjustments are deliberately left untagged: the shipping income belongs to the operator, not to any single vendor.

What this requires from your application

Both modes need ShippingMethod to be vendor-aware and the shipment/shipping-method repositories to carry the plugin's traits — in operator mode the vendor is simply always null. That wiring is mandatory in every installation and is covered in Entity and repository wiring.

Customising it

by Odiseo