Splitting Shopware 6 orders fairly across multiple suppliers

If two suppliers in your Shopware 6 shop stock the same product at the same price, who gets the order?

For most teams, the answer is: the same supplier, every single time. It’s the default. It’s simple. It’s also unfair, and it quietly makes your fulfillment brittle. When that default supplier hits a stock shortage or falls behind on shipping, you find out late, because every order in Shopware still flows through them.

Supplier rotation is how you fix that. I built this exact system as a custom Shopware 6 plugin for a shop spreading orders across multiple wholesalers and contract manufacturers. The logic is small once you strip the engineering jargon out. This is the operator-friendly version, written for owners running Shopware 6 dropshipping or manufacturing operations.

Why Shopware 6 doesn’t solve this out of the box

Shopware 6 is excellent at the order itself: the cart, the checkout, the payment, the line-item math. What it doesn’t ship with is multi-supplier routing.

The default order workflow assumes one fulfillment path. If 3 suppliers all carry the product the customer ordered, Shopware 6 has no opinion on which one gets it. Most teams paper over that by hard-coding a default supplier in their fulfillment plugin. Within a few weeks, that supplier has 90% of the volume.

Three things go wrong from there.

You lose visibility into the other suppliers. Their stock data goes stale. Their reliability stays unknown until you actually need them.

The favourite gets overloaded. Lead times stretch. Defects creep up. The thing that earned them the default slot starts to slip.

You have no fallback. The day your default supplier goes down for a long weekend or a customs holdup, you’re scrambling to redirect orders to suppliers you haven’t used in months.

Rotation prevents all three by giving every active supplier a fair, predictable slice of the work, while still respecting your preferences.

What rotation actually does

Each supplier has an order quota, a number set by your ops team in the Shopware admin. Think of it as how many orders this supplier should receive per cycle. A cycle is whatever window makes sense for your shop: a day, a week, a sprint, a campaign run.

When a new order comes in, the plugin looks at every supplier who can fulfill the whole order. It sorts them, picks the one furthest from their quota, and writes the assignment back to the order on a custom field.

Once every supplier in the rotation hits their quota, the counter resets and the cycle starts again.

That’s the whole idea. The interesting work happens around the edges.

When rotation kicks in, and when it sits out

Rotation only does something useful when there’s a clear choice between equally capable suppliers. If a customer orders 4 different products and no single supplier stocks all 4 at the right quantity, rotation can’t help. The order goes to the multi-supplier fulfillment path: each supplier gets their slice, and the rotation counter doesn’t move.

This is the single most common reason rotation looks broken. Mixed orders bypass it entirely. You can have the plugin enabled, quotas set, suppliers ready, and still see one supplier doing all the work because the order shape never gives rotation an opening.

The rule is strict. A single supplier has to stock every line item at full quantity for rotation to engage. Otherwise, the order falls through to whatever multi-supplier logic you already have.

If your catalog is mostly single-line orders (a single product per cart), rotation will fire on almost every order. If your catalog is heavily bundled or your customers buy 5+ items at once, expect rotation to trigger less often. That’s the cost of giving a clean answer in cases where the answer is clean.

How to pick the next supplier

Say 3 suppliers can fulfill the order, all under their quota. How do you choose?

Sort them on 4 fields, in this order:

  1. Priority (descending). Ops can pin a supplier higher in the admin. Useful for new partnerships, exclusive deals, or seasonal pushes.
  2. Quota (descending). A bigger quota means more capacity. Bigger capacity goes first.
  3. Current count (descending). The supplier closest to their quota goes first, so you fill capacity in order rather than spreading orders thinly across everyone.
  4. Supplier ID (ascending). A tiebreaker, so the same situation always picks the same way.

Whoever lands at the top of that sort gets the order.

The reason this works for both manufacturers and dropshippers is that those 4 fields cover the levers ops actually pulls. A manufacturer keeps their main factory near full while a backup factory ramps up. A dropshipper tests a new wholesaler with a small slice while the proven supplier carries the bulk. Same sort, different numbers.

The system has no machine learning, no forecasting, no moving averages. Operators trust what they can predict, and a 4-field sort is predictable.

If you’ve read about my Shopware 6 plugin development approach, the architecture will look familiar: decoration over inheritance, message queue for anything heavier than a request cycle, admin config over hardcoded values.

The 3 edge cases that wreck rotation in practice

Most rotation systems work fine in the picking step. They fail in the boring stuff around it. Get these three corners wrong and the plugin feels haunted.

Double-clicks and duplicate sends

Operators click “Send” twice. Networks blip. The Shopware message queue retries on failure. None of that should rotate twice and fire two orders at one supplier when they only earned one.

The fix is to make the pick step idempotent. When the order placement event fires, the plugin checks whether the order already has an assigned supplier in its custom field. If it does, it returns the stored supplier and skips the increment. The counter moves once per order, full stop.

Wrap the supplier row read in a database lock during the pick so two orders landing in the same millisecond can’t both grab the same slot. Shopware’s data layer doesn’t give you that for free. You have to write it.

When every supplier hits their quota

What happens when all eligible suppliers are at quota at the same moment? If the plugin just stops, the order stalls and a customer waits.

The right behaviour is to mark the pool for reset, then assign the order to the top supplier anyway. The actual zero-out only happens at dispatch confirmation, not when an operator previews an order. That detail matters. It stops the cycle from quietly resetting every time someone opens the form to look at it.

Manual overrides

Sometimes ops needs to pick a specific supplier outside the rotation. Maybe a customer asked for a specific factory, maybe one supplier is doing a quality test, maybe an account manager owes another supplier a favour.

An override should bump that supplier’s counter so they don’t also receive an automatic order right after, and the cycle keeps running. Treating an override as a fresh cycle corrupts the math.

Manufacturers vs. dropshippers

Same plugin, different shape.

Dropshippers typically have suppliers who overlap on hundreds of SKUs. The win is reliability and supplier health. By spreading orders, you keep all of them warm, your backup options stay tested, and no single supplier becomes the bottleneck on a Friday rush. Quotas should match each supplier’s actual fulfillment speed and quality score, not their list price.

Manufacturers usually run rotation across factories, production lines, or contract partners. The win is capacity smoothing and production planning. Quotas reflect real throughput per shift. Priority handles “this factory is a backup, not a primary.” Mixed orders rarely apply because manufacturing orders tend to be SKU-specific, so rotation kicks in on almost everything.

The mental model is the same in both cases. Multiple suppliers can do the same job. You want fair, predictable distribution that respects capacity. One knob (priority, quota) per lever you actually pull, and nothing extra in the admin.

If your business is somewhere in between, like a private-label brand with 2 manufacturers and 3 fulfillment partners, the plugin handles that too. A “supplier” is whoever you send the order to. The rotation doesn’t care about the title on their invoice.

Shipping a rotation plugin without painting yourself into a corner

If you’re scoping a custom plugin for this, the structural decisions matter more than the rotation math.

A few rules I follow on every build.

  1. Decorate, don’t override. The order placement service should be decorated. Overrides break on every minor Shopware update. Decoration survives. Same rule for the admin components where you expose quotas and priority.
  2. Push heavy work to the message queue. If your plugin makes external API calls (warehouse stock checks, ERP sync, supplier portal posts), don’t block order placement on it. Use the message queue.
  3. Store routing decisions on the order itself. Use a custom field on the order entity. The assignment becomes idempotent, queryable in the admin, and visible to ops without a custom screen.
  4. Migrations for everything. Every schema change ships with a Shopware migration. The day your plugin needs a new supplier capacity column is the day you find out whether your update method actually works.
  5. Configurable quotas in the admin. Operators set quota and priority through the supplier admin module, not a config file. If ops needs a developer to change a number, you’ll pay for it on every cycle.

I’ve shipped this kind of plugin alongside the patterns in my zero-touch order processing case study and during a pharmaceutical inventory sync where supplier reliability was the whole point of the project.

Why a simple system beats a clever one

The temptation is always to add more. Cost-based routing. Geography-based routing. SLA-based routing. Supplier-health forecasting. All of that has its place, eventually.

The core that makes any of it useful is fair distribution with predictable behaviour. When the basic rotation doesn’t work, the clever stuff on top is just more surface area to break.

Quota plus priority gives ops everything they need on day one. Bigger suppliers carry more. Pinned suppliers carry first. Override when you must. Reset when the cycle ends. The fairness math is sorting on 3 fields. That’s the whole rule book.

When you eventually do need richer routing, it slots in cleanly. The seam is already there in the decorated service.

Where to start this week

If you suspect one supplier in your Shopware 6 shop is carrying too much, do this before you write any code.

Pick your top 3 SKUs by volume. List every supplier who can fulfill each one, with realistic capacity. If only one supplier shows up across all 3, rotation can’t help. The first gap to fix is sourcing more suppliers, then rotating between them.

If 2 or more suppliers do show up, write down what you’d set as their quota and priority today. Then check whether your current Shopware 6 setup can actually enforce those numbers. If it can’t, that’s the gap worth fixing first, before going deeper into routing logic.

If you want a second set of eyes on how to wire supplier rotation into your stack, get in touch. I’ve built this plugin pattern across custom Shopware 6 builds, B2B platforms, and dropshipping operations. The hard part is rarely the code itself. The work that takes longer is making the rules match how your operators actually work.

Frequently asked questions

What is supplier rotation in Shopware 6? A custom plugin pattern that splits incoming orders across two or more suppliers who can fulfill the same product. Each supplier gets a quota set in the admin, and every new order goes to whoever is furthest from theirs. Once everyone hits their target, the cycle resets.

Does Shopware 6 have built-in multi-supplier fulfillment? No. The default Shopware 6 order workflow assumes one fulfillment path. Multi-supplier routing requires a custom plugin that hooks into the order placement flow.

When does rotation kick in? Only when one supplier can fulfill the entire order at the right quantity. Mixed orders bypass rotation and use a multi-supplier path instead. This is the most common reason rotation looks like it’s not working.

How do I pick the next supplier? Sort by priority, then quota, then current count, then supplier ID as a tiebreaker. Whoever lands on top gets the order. Wrap the read in a database lock so simultaneous orders can’t grab the same slot.

How do I avoid duplicate sends? Make the pick step idempotent. Check the order’s assigned-supplier custom field before rotating. The counter must move once per order, no matter how many retries fire.

Does this work for manufacturers and dropshippers? Both. The plugin is identical. Only the meaning of “supplier” changes: factory, 3PL, wholesaler, or marketplace partner. The rotation, quotas, priorities, and reset rules stay the same.

Share this article

Found this useful? Share it with your network

Huzaifa Mustafa

Huzaifa Mustafa

Shopware 6 certified developer with 164+ custom plugins delivered and 96+ clients across the DACH region. I write about Shopware architecture, e-commerce performance, and lessons from real projects.

Need help with Shopware?

Let's discuss how I can help with your e-commerce project.