I working on FIFO implementation in sql. I have Batch number concept in my application. If suppose I am selling on inventory then my application should tell me that which inventory is the first come. Lets. Say I purchased Inventory 'A' on 4th-Aug, 5th-Aug & 6th-Aug
On 4th Aug - A Inventory has batch number BT002 - 10 (Qty)
On 5th Aug - A's Inventory has batch number BT003 - 15 (Qty)
On 6th Aug - A's Inventory has batch number BT001 - 10 (Qty)
So, Now I am having stock Now in my hand as following :
A Inventory
BT002 - 10 - 4-Aug
BT003 - 15 - 5-Aug
BT001 - 10 - 6-Aug
Now If I want to sell that Inventory to anybody then my application should tell me that I should sell BT002 (Batch number) inventory first beacause it came first.
That was the concept I am using in my application.
Now I want to sell 15 Qty from 'A' (Inventory).
Then O/p Should be like this :
BT002 - 10
BT003 - 5
Here's My Query :
SELECT ISNULL(SUM(qty),0) AS Qty,batch_no,accept_date FROM RS_GIN_Master
GROUP BY batch_no,accept_date
HAVING ISNULL(SUM(qty),0) <= 15
ORDER BY accept_date asc
O/p Of Given Query :
How can I get O/P like this :
BT002 - 10
BT003 - 5
Any Help will be appreciated. Thank you in Advance.
This should work for you:
Working sample on Fiddle
CREATE FUNCTION [dbo].[GetBatchAmounts]
(
@requestedAmount int
)
RETURNS
@tBatchResults TABLE
(
Batch nvarchar(50),
Amount int
)
AS
BEGIN
/*This is just a mock of ersults of your query*/
DECLARE @RS_GIN_Master TABLE(
Qty int,
batch_no NVARCHAR(max),
accept_date DATETIME
)
insert into @RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT002', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(4 AS varchar) AS DATETIME)
insert into @RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT003', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(5 AS varchar) AS DATETIME)
insert into @RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT001', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(6 AS varchar) AS DATETIME)
/*---------------------------*/
DECLARE @Qty int
DECLARE @batch_no NVARCHAR(max)
DECLARE @accept_date DATETIME
DECLARE myCursor CURSOR FOR
SELECT Qty, batch_no, accept_date FROM @RS_GIN_Master ORDER BY accept_date ASC
OPEN myCursor
FETCH NEXT FROM myCursor INTO @Qty, @batch_no,@accept_date
WHILE (@@FETCH_STATUS = 0 AND @requestedAmount > 0 )
BEGIN
Declare @actualQty int
IF @requestedAmount > @Qty
SET @actualQty = @Qty
ELSE
SET @actualQty = @requestedAmount
INSERT INTO @tBatchResults (batch, Amount)
SELECT @batch_no, @actualQty
set @requestedAmount = @requestedAmount - @actualQty
FETCH NEXT FROM myCursor INTO @Qty, @batch_no,@accept_date
END /*WHILE*/
CLOSE myCursor
DEALLOCATE myCursor
RETURN
END
Just make sure to replace the marked part of the function with your query...
You need to create a stored procedure in your database and taking the quantity from your stock table. And you should also have the id of each record to update that records from where u have taken that qty.
Alter PROCEDURE sp_UpdateStockForSale
@batchNO varchar(10),
@qty decimal(9,3)
AS
BEGIN
Create Table #tmpOutput(ID int identity(1,1), StockID int, batchNo varchar(10), qty decimal(9,3));
SET NOCOUNT ON;
DECLARE @ID int;
DECLARE @Stock Decimal(9,3);
DECLARE @TEMPID int;
Select @TEMPID=(Max(ID)+1) From RS_GIN_Master Where qty > 0 And batch_no = @batchNO;
While (@qty > 0) BEGIN
Select @ID=ID, @Stock=qty From RS_GIN_Master Where qty > 0 And batch_no = @batchNO AND ID < @TEMPID Order By accept_date Desc;
--If Outward Qty is more than Stock
IF (@Stock < @qty) BEGIN
SET @qty = @qty - @Stock;
SET @Stock = 0;
END
--If Outward Qty is less than Stock
ELSE BEGIN
SET @Stock = @Stock - @qty;
SET @qty = 0;
END
Insert Into #tmpOutput(StockID,batchNo,qty)Values(@ID,@batchNO,@Stock);
SET @TEMPID = @ID;
--This will update that record don't need it now.
--Update RS_GIN_Master Set qty = @Stock Where ID=@ID
END
Select StockID, batchNo, qty From #tmpOutput;
END
GO
The above example is not compiled but, you can get the logic how you can retrieve the records from your stock table according to FIFO method. You can use accept_date
instead of ID
in RS_GIN_Master table. but, i would prefer to make it unique so, if i want to get a specific record then it can be possible.
One query .. like this
This should be tweaked for you case as you have groups and other stuff, is only for example purposes.
;with qty as (
select 15 as value
)
,l as (
select
ROW_NUMBER () over (order by accept_date desc) rn
,*
from xxx
)
,q as (
select
batch_no
,accept_date
,case when value>qty then value-qty else 0 end as remainder
,case when value>qty then qty else value end as used
,rn
from l
cross join qty
where rn=1
union all
select
r.batch_no
,r.accept_date
,case when q.remainder>r.qty then q.remainder-r.qty else 0 end as remainder
,case when q.remainder>r.qty then r.qty else q.remainder end as used
,r.rn
from q
join l r
on q.rn+1 = r.rn
where q.remainder!=0
)
select *
from q
where used != 0
and the fiffle for it http://sqlfiddle.com/#!6/9b063/34/0
Below should work for you
Create table RS_GIN_Master
(id int,
qty int,
batch_no varchar(5),
accept_date Datetime
)
GO
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(1,10,'BT001','2018-04-06')
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(2,10,'BT002','2018-04-04')
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(3,15,'BT003','2018-05-06')
GO
----------------------------
CREATE PROC FIFO
@TakenQty int
AS
BEGIN
WITH cte AS (SELECT *, SUM(qty) OVER (ORDER BY accept_date, id ASC) as CumQty FROM RS_GIN_Master WHERE qty>0)
SELECT TOP ((SELECT COUNT(*) FROM cte WHERE CumQty <@TakenQty)+1) batch_no, accept_date,
CASE
WHEN CumQty<@TakenQty THEN qty
ELSE @TakenQty -(CumQty-Qty)
END AS TakenOut
FROM cte
END
Result
| batch_no | accept_date | TakenOut |
|----------|----------------------|----------|
| BT002 | 2018-04-04T00:00:00Z | 10 |
| BT001 | 2018-04-06T00:00:00Z | 5 |
http://www.sqlfiddle.com/#!18/f7ee7/1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With