Skip to content

Shipping & Fulfillment

Vectis handles shipping through a carrier strategy pattern and supports simple single-shipment orders as well as multi-box warehouse fulfillment.

Carrier Strategies

Each shipping carrier is an extension that implements the ShippingCalculatorStrategy interface. The system ships with two built-in strategies:

Strategy Behavior
FlatRateShippingStrategy Returns a fixed rate per shipping zone
FreeShippingStrategy Returns a zero-cost rate (typically activated by promotions)

Custom carriers (UPS, FedEx, local courier) are added as extensions that query live rate APIs and return structured rate quotes.

Rate Quoting

When a buyer reaches the shipping step at checkout, the storefront resolves rates in phases so slow carriers never block fast ones:

  1. Flat rates load instantly (shippingRatesForCheckout(rateType: "flat")) — no external API calls.
  2. Live rates load per carrier, in parallel. The checkout page first asks liveRateCarriersForCheckout which live-rate providers apply (same zone, channel, and restriction filters as the rates query), then fires one shippingRatesForCheckout(rateType: "live", carrier: <code>) request per carrier. Each carrier's rates appear in the list as soon as that carrier responds — USPS local rate tables render in milliseconds while an LTL broker like Priority1 may take several seconds to fill in.

On the backend, when a single request covers multiple live providers, packing and method preparation run first, then all carrier API calls execute concurrently — the response takes as long as the slowest carrier, not the sum of all of them.

Each strategy returns zero or more rate options (e.g., "Ground — $8.50", "Express — $22.00"). The buyer selects one. (ShippingService.get_available_rates(), which queries all registered strategies, remains for admin cart flows.)

Tip

Carriers can be scoped to specific channels or shipping zones. A channel selling only in Mexico does not need to query a US-only carrier.

Shipping Zones and Methods

Shipping configuration is organized into three levels:

  1. Shipping Providers — represent a carrier or logistics partner (e.g., "FedEx", "In-House Delivery").
  2. Shipping Zones — geographic regions a provider serves (e.g., "Domestic", "North America", "EU").
  3. Shipping Methods — specific service levels within a zone (e.g., "Ground", "2-Day", "Overnight").

Each method links to a carrier strategy that computes the rate.

Fulfillment Modes

Vectis supports two fulfillment modes, configured per channel:

By Order (Simple)

The entire order ships as one unit. One tracking number, one carrier. Best for small operations or digital-heavy catalogs.

By Box (Warehouse)

Orders are fulfilled box-by-box. Each box:

  • Is created from a box template (dimensions, weight limit) or custom dimensions.
  • Has its own tracking number and optional carrier.
  • Contains specific line items and quantities.

This mode supports warehouse scanning workflows where packers scan items into boxes and the system tracks per-box fulfillment.

graph TD
    O[Order] --> B1[Box 1 — FedEx Ground]
    O --> B2[Box 2 — FedEx Ground]
    O --> B3[Box 3 — Freight LTL]
    B1 --> T1[Tracking: 1Z999...]
    B2 --> T2[Tracking: 1Z888...]
    B3 --> T3[Tracking: PRO-4455]

Note

An order in by-box mode transitions to shipped only when all boxes have tracking numbers assigned. Partially fulfilled orders remain in processing.

Free Shipping

Free shipping is a real shipping method — a $0 flat-rate method (the seeded generic:free_shipping "Free Shipping" method) that participates in the same controls as every other method. Its availability composes three independent switches:

Control Mechanism Answers
Where Zone assignment (Settings → Shipping → Zones) Which destinations offer it
Who Restriction rules on the method code Which customer groups / accounts / roles see it
When Requires promotion flag + an active free-shipping promotion Whether it needs a promo to unlock
  • Always-on free shipping for a zone or customer group: leave Requires promotion off, assign the method to the zone, and (optionally) add a restriction rule scoping it to a group. No promotion needed.
  • Threshold or coupon free shipping (e.g., "free over $500"): keep Requires promotion on and create a discount rule with the condition (cart subtotal ≥ $500, or a coupon code) and the Free shipping action. The method appears at checkout only while the promotion applies. Other methods keep their price — buyers can still choose a paid faster option.
  • Discounting other methods: the promotion's Eligible shipping methods picker can target specific paid methods instead (e.g., 50% off Ground). Unchecked methods are never discounted. See Promotions — Shipping Discounts.

At checkout the money path verifies the gate server-side: a promo-gated method submitted without a qualifying promotion on the cart is ignored and re-priced, so free shipping can never be obtained by replaying a stale selection.

Box Templates

Box templates define reusable packaging configurations:

Field Purpose
name Display name (e.g., "Small Box", "Pallet")
length, width, height Dimensions for rate calculation
max_weight Weight limit for packing validation

Templates speed up warehouse fulfillment — packers select a template and the system pre-fills dimensions.

Admin Panel

From Settings → Shipping in the admin:

  • Providers — create and configure shipping providers.
  • Zones — define geographic zones and assign them to providers.
  • Methods — add service levels within zones, link to carrier strategies, set rate parameters.
  • Box Templates — manage reusable packaging templates.

From the Order detail → Fulfillment tab:

  • Create shipments (by-order) or pack boxes (by-box).
  • Enter tracking numbers and select carriers.
  • Mark fulfillments as shipped.

Warning

Changing a provider's strategy after orders have been fulfilled does not alter historical shipping records. Rate recalculation only affects future checkouts.

Background Rate Precompute

Rate quotes are precomputed in the background on cart changes and cached in Valkey (Redis-compatible). The precompute key is composed deterministically from cart subtotal, ship-to ZIP / region, cubing dimensions, and the carrier set; an unchanged cart hits the cache while a changed cart re-queues precompute. Behaviour is controlled by:

  • shipping_precompute_enabled — global toggle setting (default true)

Cart-change events are coalesced with a short debounce so a burst of edits fires a single precompute against the settled cart. The debounce window is fixed at 0.3s in fulfillment/precompute.py (_KICK_DEBOUNCE_SECONDS) and is not configurable.

The asyncio loop now yields between rate provider calls so a slow third-party provider can't block the cart write itself (commit f17bd16).

Cubing Engine

The cubing engine pre-packs cart items into boxes for accurate dimensional quotes. Two modes:

  • Global — single dimensional volume across all providers
  • Per-provider — re-run cubing for each provider's box catalog (useful when carriers have wildly different free-box sizes)

Mode is set per provider via provider.cubing_mode. Cubing dedups results across identical inputs to avoid recomputing the same pack for multiple rate calls.

Items flagged track_inventory=false (untracked products) are excluded from cubing — the system can't know what physical packaging the supplier ships, so live rates ignore them and the order surfaces a "ship-from-supplier" message instead.

Carrier Auto-Seed

When a channel's allowed_carriers list is updated, Vectis auto-seeds default shipping methods for each carrier on first save (UPS Ground, USPS Priority, etc.). This means activating UPS for a new channel doesn't require a separate "add methods" step.

Extensions can register their own carrier_seed entries — when the extension is activated, its declared methods are seeded automatically.

AfterShip → Fulfillment Category

AfterShip moved from the "Shipping" category to Fulfillment (commit da0685f). The change is purely organizational — AfterShip is a tracking provider, not a rate quoter. The extension now registers its tracking-poll schedule via register_schedule, so installing it spins up the recurring poll automatically.

RMA & Refund Shipping Policy

Returns and exchanges now obey the channel's shipping refund policy (vectis/modules/rma/models.py):

  • shipping_refund_amount — the shipping amount refunded on the return.
  • exchange_pricing_mode — how an exchange is repriced (defaults to honor_original).

The RMA module reads these fields at decision time so refund totals match the merchant's stated policy.