Skip to content

Transfer Order#11281

Draft
jacobfelknor wants to merge 44 commits intoinventree:masterfrom
jacobfelknor:transfer_order
Draft

Transfer Order#11281
jacobfelknor wants to merge 44 commits intoinventree:masterfrom
jacobfelknor:transfer_order

Conversation

@jacobfelknor
Copy link
Contributor

@jacobfelknor jacobfelknor commented Feb 10, 2026

This PR should still be considered a draft, but has been opened in order to facilitate review.

It adds a Transfer Order to InvenTree which is based on other order types, such as a Sales Order.

Done:

  • On create, we select a source and destination location
  • Add line items, selecting the part objects you need to transfer
  • Allocate stock to each line item
    • like a Build Order, each allocation defaults to the source location of the order, but may be overridden in the allocate form
  • Upon completion of the order, all allocated stock is transferred to the destination location
    • you may optionally accept incomplete allocations in the complete order form
  • When completed, the "allocated stock" tab and table changes to "transferred stock"
  • There is a sample default report included
  • settings for TRANSFERORDER_ENABLED, TRANSFERORDER_REFERENCE_PATTERN, and TRANSFERORDER_REQUIRE_RESPONSIBLE
  • Transfer order rule set, so permissions can be assigned

Differences between Transfer Order and other Orders

  • no "extra lines"
  • no shipment tracking, like a Sales Order
  • no customer

Items I'd still like to explore

  • Adding a "consume" option which consumes stock against an order rather than moving it.

    • this is admittedly a bit strange, but we have scenarios, especially against consumables, where we're interested in queuing up stock to release from inventory for review, then want record (a report) of it occurring.
    • Still undecided whether its better to just manually remove the stock after the transfer takes place. The fact that it all exists in the "transferred stock" panel means it wouldn't be too hard to delete it post-transfer, but explicitly adding a "consume" option would make the intention more clear
  • Immutable tracking on the transfer.

    • right now, the "transferred stock" panel changes over time due to this being a direct link to the stock item.
    • preferably, this panel can show forever which stock was transferred, independent of further operations
    • this behavior isn't unique to TOs. Purchase Orders also exhibit this behavior on received stock
    • must do it in a way that retains the user's ability to navigate to that stock item if it still exists.
    • the fact that the report can operate on the original line items, not the transferred stock itself, makes this less of a priority than I had originally envisioned.
      • however, using line items means we still loose track of some information, such as which batch code was transferred
  • Similarly to BO's "consume stock" feature, it may be nice to execute the Transfer Order in smaller increments. We could add a "transfer now" feature to allocated stock, doing partial transfers before the entire order is complete

  • add a Transfer Order Allocations table to the stock item Allocations panel

  • unit testing

  • documentation

General questions

  • Should transfer orders update the "quantity required" number on parts if we want to transfer more than what is available?
    • I hadn't considered this before, because my assumption was that TOs were only intended to operate on existing stock.
    • If the TO is used like a request (hey, engineering needs 5 of these by this date), then hooking this in the quantity required may be helpful.
  • Similarly, do allocations to TOs reduce the available stock for an item until the order is completed, upon which it restores this qty to available
    • IN_STOCK_FILTER would need to account for transfer orders in this case

Testing

  • No unit tests exist for this yet. Of course, these will need to be added

@netlify
Copy link

netlify bot commented Feb 10, 2026

Deploy Preview for inventree-web-pui-preview failed.

Name Link
🔨 Latest commit 15d4674
🔍 Latest deploy log https://app.netlify.com/projects/inventree-web-pui-preview/deploys/69a214fdee75e6000869c853

@jacobfelknor jacobfelknor mentioned this pull request Feb 10, 2026
2 tasks
@matmair matmair added enhancement This is an suggested enhancement or new feature order Related to purchase orders / sales orders User Interface Related to the frontend / User Interface api Relates to the API labels Feb 10, 2026
@matmair matmair added this to the 1.3.0 milestone Feb 10, 2026
Copy link
Member

@matmair matmair left a comment

Choose a reason for hiding this comment

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

Very good start; looking at this we might want to change how a few parts of the code base work, this seems much to complicated for someone new to wrap their head around. Thanks for doing so still

),
# API endpoints for transfer orders
path(
'to/',
Copy link
Member

Choose a reason for hiding this comment

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

I think we should strive for a more human-readable API so transfer-order would be better here IMO

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tend to agree. I matched the pattern already present, such as so/ for Sales Orders, ro/ for Return Orders, etc. I don't think I should change all API paths, so would you rather me match existing patterns or have Transfer Order diverge from the pattern but start more human-readable?

Copy link
Member

Choose a reason for hiding this comment

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

Currently starting to write API docs in inventree/project-adr#4 that make things like this clearer; I think starting to diverge would be good. One less thing that we will need to break

return sum([
self.build_order_allocation_count(**kwargs),
self.sales_order_allocation_count(**kwargs),
self.transfer_order_allocation_count(**kwargs),
Copy link
Member

Choose a reason for hiding this comment

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

are we sure we want to include this here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure. This starts to creep in my question about whether we want to consider stock allocated to a Transfer Order as "available" or not, which also impacts the "required quantity"

Copy link
Member

Choose a reason for hiding this comment

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

It is an interesting question. I think that stock which is "in transfer" is not necessarily "unavailable" - merely moving from one location to another?

Alternatively if they are "unavailable" until the transfer order is complete, this also could make sense because it precludes them being assigned elsewhere (until the transfer is completed)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here's an argument against including "reduced availability" logic for Transfer Orders.....

The spot it hurts most is actually not at the part allocation counts, referenced here, but at the stock item allocation count in StockItemSerializer.annotate_queryset(). The part level allocation counts at least are "released" once the order is no longer "pending", as defined by TransferOrderStatusGroups and Part.transfer_order_allocations()

However, if I don't clear allocations, the available stock remains reduced even after the order completes from the point of view of the stock item.

   @staticmethod
    def annotate_queryset(queryset):
        ....
        # Annotate the queryset with the total allocated to sales orders
        queryset = queryset.annotate(
            allocated=Coalesce(
                SubquerySum('sales_order_allocations__quantity'), Decimal(0)
            )
            # THIS IS THE PROBLEMATIC LINE!
            # this count isn't 'released' after the TO completes, 
            # forever reducing available qty for a stock item that has been through a TO
            + Coalesce(SubquerySum('transfer_order_allocations__quantity'), Decimal(0))
            + Coalesce(SubquerySum('allocations__quantity'), Decimal(0))
        )

And since in the current implementation, the TransferOrderAllocation is the only thing attaching a StockItem to a TransferOrder, if I clear allocations on complete, I lose the reference for which stock was transferred from this order.

In lieu of a more sophisticated immutable tracking on the transfer order - which I'm beginning to think is more scope than I'm willing to commit to in this PR - I think excluding TO allocations from any availability calculations is best, and then revisit when/if the immutable tracking exists.

@matmair matmair marked this pull request as draft February 10, 2026 20:54
@jacobfelknor
Copy link
Contributor Author

I'll be on vacation for the next 10 days, so won't be working on this until I'm back.

Before spending any more serious time on it when I return, I'd like to hear from your perspective what should be my priorities before this can be merged. I've detailed some of my thoughts in the PR description.

The basic functionality present is probably enough for me to start using it on my instance - maybe behind a feature flag - but I know there's a lot of housekeeping left to do before it could be considered "official."

@SchrodingersGat
Copy link
Member

Documentation

@jacobfelknor this will need to also have comprehensive docs added, under the "stock" master docs location :)

Copy link
Member

@SchrodingersGat SchrodingersGat left a comment

Choose a reason for hiding this comment

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

@jacobfelknor this is a really good implementation, great effort!

return sum([
self.build_order_allocation_count(**kwargs),
self.sales_order_allocation_count(**kwargs),
self.transfer_order_allocation_count(**kwargs),
Copy link
Member

Choose a reason for hiding this comment

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

It is an interesting question. I think that stock which is "in transfer" is not necessarily "unavailable" - merely moving from one location to another?

Alternatively if they are "unavailable" until the transfer order is complete, this also could make sense because it precludes them being assigned elsewhere (until the transfer is completed)

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

Labels

api Relates to the API enhancement This is an suggested enhancement or new feature order Related to purchase orders / sales orders User Interface Related to the frontend / User Interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants