I want value the cost of goods sold using the FIFO method.
I know how many beers I sold. Based on my price I bought those beers at, what is the cost of those sales? So, my sales of 7 Peronis are valued at £1.70 -- based on the FIFO valuation method.
How do I calculate in SQL Server.
I am going to be working this out for many products and from many branches at the same time, so I would like to use a method that does not involve cursors (or any other types of loops).
-- SETUP
DROP TABLE IF EXISTS #Deliveries;
CREATE TABLE #Deliveries (DeliveryDate DATE, ProductCode VARCHAR(10), Quantity INT, Cost DECIMAL(6,2));
INSERT INTO #Deliveries (DeliveryDate, ProductCode, Quantity, Cost)
VALUES
('2020-11-23', 'PERONI', 2, 0.20), ('2020-11-24', 'PERONI', 4, 0.30), ('2020-11-25', 'PERONI', 7, 0.10),
('2020-11-23', 'BUDWEISER', 5, 0.20), ('2020-11-24', 'BUDWEISER', 5, 0.50), ('2020-11-25', 'BUDWEISER', 4, 0.80);
DROP TABLE IF EXISTS #StockResults;
CREATE TABLE #StockResults (ProductCode VARCHAR(10), SalesQty INT, CostOfSalesValue DECIMAL(6,2));
INSERT INTO #StockResults (ProductCode, SalesQty)
VALUES ('PERONI', 7), ('BUDWEISER', 4);
SELECT * FROM #Deliveries;
SELECT * FROM #StockResults;
-- DESIRED RESULT
/*
ProductCode SalesQty CostOfSalesValue
PERONI 7 1.70
BUDWEISER 4 0.80
*/
This is probably not very efficient but it shows you one way in which this can be achieved which should help you come up with your finished solution. I would imagine that there needs to be a lot more complexity built into this process to account for things like stock wastage, but I'll leave that up to you:
-- SETUP
declare @Deliveries table (DeliveryDate date, ProductCode varchar(10), Quantity int, Cost decimal(6,2));
insert into @Deliveries (DeliveryDate, ProductCode, Quantity, Cost) values ('2020-11-23', 'PERONI', 2, 0.20), ('2020-11-24', 'PERONI', 4, 0.30), ('2020-11-25', 'PERONI', 7, 0.10),('2020-11-23', 'BUDWEISER', 5, 0.20), ('2020-11-24', 'BUDWEISER', 5, 0.50), ('2020-11-25', 'BUDWEISER', 4, 0.80);
declare @StockResults table (ProductCode varchar(10), SalesQty int);
insert into @StockResults (ProductCode, SalesQty) values ('PERONI', 7), ('BUDWEISER', 4);
-- QUERY
with r as
(
select d.ProductCode
,d.DeliveryDate
,d.Quantity
,d.Cost
,isnull(sum(d.Quantity) over (partition by d.ProductCode order by d.DeliveryDate rows between unbounded preceding and 1 preceding),0) as RunningQuantityStart
,sum(d.Quantity) over (partition by d.ProductCode order by d.DeliveryDate) as RunningQuantityEnd
from @Deliveries as d
)
select r.ProductCode
,s.SalesQty
,sum(case when r.RunningQuantityEnd >= s.SalesQty
then (s.SalesQty - r.RunningQuantityStart) * r.Cost
else (r.RunningQuantityEnd - r.RunningQuantityStart) * r.Cost
end
) as CostOfSalesValue
from r
join @StockResults as s
on r.ProductCode = s.ProductCode
and r.RunningQuantityStart < s.SalesQty
group by r.ProductCode
,s.SalesQty;
+-------------+----------+------------------+
| ProductCode | SalesQty | CostOfSalesValue |
+-------------+----------+------------------+
| BUDWEISER | 4 | 0.80 |
| PERONI | 7 | 1.70 |
+-------------+----------+------------------+