Skip to content

Reuse the order's line items when building a shipping manifest - #6524

Open
mamhoff wants to merge 3 commits into
solidusio:mainfrom
mamhoff:in-memory-manifest
Open

Reuse the order's line items when building a shipping manifest#6524
mamhoff wants to merge 3 commits into
solidusio:mainfrom
mamhoff:in-memory-manifest

Conversation

@mamhoff

@mamhoff mamhoff commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Spree::ShippingManifest builds its items from inventory units, and reaches the line
item through unit.line_item. Since Rails has no identity map, that returns a second
instance of a row the caller almost always already holds in memory as part of
order.line_items.

Two consequences:

  1. Stale data. Adjustments recalculated on the order's line items but not yet
    persisted are invisible to the manifest, which reports the values as they were on
    disk. This is the bug that motivated the change.
  2. A query per line item, on a path that is usually rendered once per shipment.

This adds an optional order: keyword to ShippingManifest#initialize. When given, and
when that order's line items are already loaded, the manifest resolves each item's line
item from the order instead of from the inventory unit. It falls back to unit.line_item
otherwise, so behaviour is unchanged for every existing caller.

Implementation notes

  • Query-neutral by construction. The reuse is skipped entirely unless
    order.association(:line_items).loaded?. It is an optimization for callers that already
    have the line items, never a reason to issue a query.
  • For the same reason, Shipment#manifest passes order: (order if association(:order).loaded?) rather than order: order — it must not trigger a load of
    the order. In the common path (order.shipments) the association is already populated
    via inverse_of, so this costs nothing.
  • Nothing is mutated: the lookup happens where the manifest item is constructed, so no
    association targets are reassigned and no inventory unit is touched.
  • for_order propagates the order, which is how Carton#manifest_for_order picks this
    up. Carton#manifest is deliberately left alone — a carton can span multiple orders,
    so there is no single order to pass.
  • nil line_item_id (the association is optional: true) and units belonging to a
    different order both fall through to the existing behaviour via Hash#fetch with a
    block.
  • The group_by(&:variant_id) / group_by(&:line_item_id) optimization in items is
    preserved. This arguably extends it: the one remaining association call per group now
    resolves from memory too.

Call sites updated

  • Spree::Shipment#manifest
  • spree/admin/orders/_shipment — this one builds the manifest from
    shipment.inventory_units.where(carton_id: nil), and already has order in scope.

Behaviour change to be aware of

A manifest built with order: now surfaces in-memory, unsaved changes to line items.
That is the intent, but it is observable: anything relying on the manifest's line item
being a clean read of the row will see the order's working copy instead.

Why not inverse_of?

InventoryUnit and LineItem already declare it on both sides, but inverse_of only
links objects loaded through one another. The shipment → inventory units → line item
path never passes through order.line_items.

Not addressed

first_unit.variant has the same shape and gets no equivalent treatment. Variants are
not mutated in memory during order recalculation the way adjustments are, so there is no
correctness bug there — only a preloading question, which callers can handle with
includes.

Checklist

Check out our PR guidelines for more details.

The following are mandatory for all PRs:

The following are not always needed:

  • 📖 I have updated the README to account for my changes.
  • 📑 I have documented new code with YARD.
  • 🛣️ I have opened a PR to update the guides.
  • ✅ I have added automated tests to cover my changes.
  • 📸 I have attached screenshots to demo visual changes.

mamhoff added 3 commits July 29, 2026 15:07
With the new in-memory order updater and especially SolidusPromotions,
the shipment manifest needs to be aware of any in-memory changes to line
items. This adds code that allows the manifest to load line items from a
specific order.
When creating a manifest, this uses the Spree::ShippingManifest's new
ability to re-use already loaded line items for the shipping manifest
items.
This uses the same optimization from before to use loaded line items in
the shipment manifest (for unpacked items).
@mamhoff
mamhoff requested a review from a team as a code owner July 29, 2026 13:17
@github-actions github-actions Bot added changelog:solidus_backend Changes to the solidus_backend gem changelog:solidus_core Changes to the solidus_core gem labels Jul 29, 2026
@mamhoff mamhoff added the backport-v4.7 Backport this pull-request to v4.7 label Jul 29, 2026
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.03%. Comparing base (e4e7df1) to head (61f9317).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6524   +/-   ##
=======================================
  Coverage   92.02%   92.03%           
=======================================
  Files        1035     1035           
  Lines       21141    21152   +11     
=======================================
+ Hits        19456    19467   +11     
  Misses       1685     1685           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@tvdeyen tvdeyen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-v4.7 Backport this pull-request to v4.7 changelog:solidus_backend Changes to the solidus_backend gem changelog:solidus_core Changes to the solidus_core gem

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants