Bug 41959

Summary: Holds queue builder doesn't always check all holds when using transport cost matrix
Product: Koha Reporter: Nick Clemens (kidclamp) <nick>
Component: Hold requestsAssignee: Bugs List <koha-bugs>
Status: NEW --- QA Contact: Testopia <testopia>
Severity: major    
Priority: P5 - low CC: gmcharlt, lisette, tomascohen
Version: Main   
Hardware: All   
OS: All   
GIT URL: Initiative type: ---
Sponsorship status: --- Comma delimited list of Sponsors:
Crowdfunding goal: 0 Patch complexity: ---
Documentation contact: Documentation submission:
Text to go in the release notes:
Version(s) released in:
Circulation function:
Bug Depends on: 35826    
Bug Blocks:    

Description Nick Clemens (kidclamp) 2026-02-28 18:48:52 UTC
The scenario presented itself under a specific circumstance

Library A ordered a copy of a book and left in 'on order' status
Library B purchased a copy and made it available

Each library can only fill holds on their own items
The transport cost matrix only allows items to transfer to their own branch
The transfer limits only allows items to transfer to their own branch

A patron of Library A places a title level hold, it can't fill because library A's item is unavailable (on order)

A patron of Library B places a title level hold, it should be queued for library B's item

In C4::HoldsQueue::MapItemsToHoldRequests we call _allocateWithTransportCostMatrix

In _allocateWithTranportCostMatrix we have this code:
    if ( $num_tasks > $num_agents ) {
        @remaining = @requests[ $num_agents .. $num_tasks - 1 ];
        @requests  = @requests[ 0 .. $num_agents - 1 ];
        $num_tasks = $num_agents;
    }

task = requests and agents = items

In our scenario - 2 tasks/requests and 1 item/agent

The code above drops the second request, and sets tasks to 1

We then loop tasks and agents:
        for ( my $i = 0 ; $i < $num_agents ; $i++ ) {
            for ( my $j = $r ; $j < $num_tasks ; $j++ ) {

But since we have dcereased things above, we only check the first task/request - the second hold, which should be filled, is never checked