In future please provide consumable test data:
SET ANSI_NULLS, QUOTED_IDENTIFIER, ANSI_PADDING ON;
GO
CREATE TABLE #Givers
(
Product char(1) NOT NULL
,QtyAllowedToGive int NOT NULL
,Warehouse_ID_Giver int NOT NULL
,[Rank] int NOT NULL PRIMARY KEY
);
INSERT INTO #Givers
VALUES ('A', 30, 100, 1)
,('A', 30, 101, 2)
,('A', 10, 102, 3)
,('B', 10, 200, 4)
,('B', 20, 201, 5)
,('D', 2, 300, 6);
CREATE TABLE #Takers
(
Product char(1) NOT NULL
,QtyAllowedToTake int NOT NULL
,Warehouse_ID_Taker int NOT NULL
,[Rank] int NOT NULL PRIMARY KEY
);
INSERT INTO #Takers
VALUES ('A', 50, 200, 1)
,('A', 5, 203, 2)
,('A', 11, 202, 3)
,('B', 16, 202, 4)
,('B', 3, 202, 5)
,('C', 10, 300, 6);
One approach is to brute force the result with a number/tally table.
eg Using Create a Tally Function (fnTally) – SQLServerCentral
WITH Givers
AS
(
SELECT G.Product, G.Warehouse_ID_Giver, G.[Rank]
,ROW_NUMBER() OVER (PARTITION BY G.Product ORDER BY G.[Rank], N.N) AS rn
FROM #Givers G
JOIN dbo.fnTally(1, 1000) N
ON N.N <= G.QtyAllowedToGive
)
,Takers
AS
(
SELECT T.Product, T.Warehouse_ID_Taker, T.[Rank]
,ROW_NUMBER() OVER (PARTITION BY T.Product ORDER BY T.[Rank], N.N) AS rn
FROM #Takers T
JOIN dbo.fnTally(1, 1000) N
ON N.N <= T.QtyAllowedToTake
)
SELECT G.Product
,COUNT(*) AS QtyToMove
,G.Warehouse_ID_Giver, T.Warehouse_ID_Taker
FROM Givers G
JOIN Takers T
ON G.Product = T.Product
AND G.rn = T.rn
GROUP BY G.Product, G.Warehouse_ID_Giver, T.Warehouse_ID_Taker
ORDER BY G.Product, MAX(G.[Rank]), MAX(T.[Rank]);