Inventory Aging Report with Adjustment Support (FIFO) - sql

Have a problem to solve in the SQL Server to generate a Inventory aging report using FIFO Based on SKU & Warehouse with adjustment support (incoming (+ve) and outgoing (-ve)). I have attached the schema here.
SKU
TransactionType
WarehouseCode
TransactionDate
Qty
100
IN
WH1
2021-04-30
100
100
IN
WH2
2021-04-30
50
101
IN
WH1
2021-04-30
30
101
IN
WH2
2021-05-01
25
100
OUT
WH2
2021-05-02
30
100
OUT
WH1
2021-05-02
20
100
OUT
WH1
2021-05-04
50
100
OUT
WH2
2021-05-04
20
100
OUT
WH1
2021-05-05
25
100
IN
WH2
2021-05-10
30
100
IN
WH1
2021-05-11
30
101
OUT
WH2
2021-05-12
20
100
OUT
WH1
2021-05-15
30
102
IN
WH2
2021-05-15
25
102
OUT
WH2
2021-05-17
2
102
ADJ
WH2
2021-05-18
5
102
ADJ
WH2
2021-05-18
-1
Based on the above schema structure, i need to develop a inventory aging report based on first in first out (FIFO) and show the remaining qty of each SKU and Warehouse combination and make the previous incoming records remaining quantities as zero.
Expected report format Assuming the report is run on (2021-05-20)
SKU
TransactionType
WarehouseCode
TransactionDate
Qty
Remaining
Aging
100
IN
WH1
2021-04-30
100
0
21
100
IN
WH2
2021-04-30
50
0
21
101
IN
WH1
2021-04-30
30
30
21
101
IN
WH2
2021-05-01
25
5
20
100
IN
WH2
2021-05-10
30
30
11
100
IN
WH1
2021-05-11
50
5
10
102
IN
WH2
2021-05-15
25
22
6
102
IN
WH2
2021-05-18
5
5
3
Got the inventory aging report with SQL window function, need assistance adding support for adjustment.
WITH
cumulative AS
(
SELECT
*,
SUM(CASE WHEN TransactionType = 'IN' THEN Qty ELSE 0 END)
OVER (
PARTITION BY SKU, WarehouseCode
ORDER BY TransactionDate
)
AS qty_in_so_far,
SUM(CASE WHEN TransactionType = 'OUT' THEN Qty ELSE 0 END)
OVER (
PARTITION BY SKU, WarehouseCode
)
AS qty_out_final
FROM
inventory
)
SELECT
*,
CASE WHEN qty_out_final > qty_in_so_far THEN 0
WHEN qty_in_so_far - qty_out_final > qty THEN qty
ELSE qty_in_so_far - qty_out_final END AS qty_final,
DATEDIFF(day, TransactionDate, '2021-05-20') + 1 AS aging
FROM
cumulative
WHERE
TransactionType = 'IN'
ORDER BY
TransactionDate,
SKU,
WarehouseCode
SQL Fiddle for sample schema and query - https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=de36b2c43d16a0e1e69dd64561422525

Something like this might do it, for the limited data provided.
The first CTE terms tie the 'ADJ' rows to the corresponding prior 'IN' rows, and later simply SUM those group Qtys to derive final 'IN' rows which have the adjustments applied.
Now we use your original logic (with slight corrections) to handle the rest, no longer needing to worry about adjustments.
Limitation: If adjustment rows exist which completely remove more than the prior "IN" transaction, that will require more logic. Unless you need that, the following should be fine.
Also note: Some of your original "Aging" results (in the question) appear wrong, and are not based on the corresponding "IN" row.
The updated fiddle
WITH cte1 AS (
SELECT i.*
, SUM(CASE WHEN TransactionType = 'IN' THEN 1 ELSE 0 END) OVER (PARTITION BY SKU, WarehouseCode ORDER BY TransactionDate) AS grp
FROM inventory AS i
WHERE TransactionType IN ('IN', 'ADJ')
)
, cte2 AS (
SELECT SKU, WarehouseCode, 'IN' AS TransactionType
, MIN(TransactionDate) AS TransactionDate
, SUM(Qty) AS Qty
FROM cte1
GROUP BY WarehouseCode, SKU, grp
UNION
SELECT SKU, WarehouseCode, TransactionType, TransactionDate, Qty
FROM inventory
WHERE TransactionType = 'OUT'
)
, cumulative AS (
SELECT *
, SUM(CASE WHEN TransactionType = 'OUT' THEN Qty ELSE 0 END) OVER (PARTITION BY SKU, WarehouseCode) AS qty_out_final
, SUM(CASE WHEN TransactionType = 'IN' THEN Qty ELSE 0 END) OVER (PARTITION BY SKU, WarehouseCode ORDER BY TransactionDate) AS qty_in_so_far
FROM cte2
)
SELECT SKU, WarehouseCode, TransactionType, TransactionDate
, qty_out_final, qty_in_so_far, Qty
, CASE WHEN qty_out_final >= qty_in_so_far THEN 0
ELSE qty_in_so_far - qty_out_final END AS qty_final
, DATEDIFF(day, TransactionDate, '2021-05-20') + 1 AS aging
FROM cumulative
WHERE TransactionType = 'IN'
ORDER BY TransactionDate, SKU, WarehouseCode
;
The result:
SKU
WarehouseCode
TransactionType
TransactionDate
qty_out_final
qty_in_so_far
Qty
qty_final
aging
100
WH1
IN
2021-04-30
125
100
100
0
21
100
WH2
IN
2021-04-30
50
50
50
0
21
101
WH1
IN
2021-04-30
0
30
30
30
21
101
WH2
IN
2021-05-01
20
25
25
5
20
100
WH2
IN
2021-05-10
50
80
30
30
11
100
WH1
IN
2021-05-11
125
130
30
5
10
102
WH1
IN
2021-05-15
2
29
29
27
6
100
WH1
IN
2021-05-16
125
160
30
35
5

Related

Sales amounts of the top n selling vendors by month with other fields in bigquery

i have a table in bigquery like this (260000 rows):
vendor date item_price discount_price
x 2021-07-08 23:41:10 451,5 0
y 2021-06-14 10:22:10 41,7 0
z 2020-01-03 13:41:12 74 4
s 2020-04-12 01:14:58 88 12
....
exactly what I want is to group this data by month and find the sum of the sales of only the top 20 vendors in that month. Expected output:
month vendor_name(top20) sum_of_vendor's_sales sum_of_vendor's_discount item_count(sold)
2020-01 x1 10857 250 150
2020-01 x2 9685 410 50
2020-01 x3 3574 140 45
....
2021 01 x20 700 15 20
2020-02 y1 7421 280 120
2020-02 y2 6500 250 40
2020-02 y3 4500 200 70
.....
2020-02 y20 900 70 30
i tried this (source here). But The desired output could not be obtained.
select month,
(select sum(sum) from t.top_20_vendors) as sum_of_only_top20_vendor_sales
from (
select
format_datetime('%Y%m', date) month,
approx_top_sum(vendor, item_price, 20) top_20_vendors,count(item_price) as count_of_items,sum(discount_price)
from my_table
group by month
) t
Consider below approach
select
format_datetime('%Y%m', date) month,
vendor as vendor_name_top20,
sum(item_price) as sum_of_vendor_sales,
sum(discount_price) as sum_of_vendor_discount,
count(*) as item_count_sold
from your_table
group by vendor, month
qualify row_number() over(partition by month order by sum_of_vendor_sales desc) <= 20

Multiple Between Dates Aggregation

I have this dataset that is structured more or less like the following:
Product
Sales Value
Sales Qty
Sales Date
Period 1 start
Period 1 end
Period 2 start
Period 2 end
XXX
6
2
2021-05-20
2021-05-15
2021-05-21
2021-05-22
2021-06-01
YYY
10
3
2021-05-21
2021-05-15
2021-05-21
2021-05-22
2021-06-01
XXX
3
1
2021-05-23
2021-05-15
2021-05-21
2021-05-22
2021-06-01
XXX
6
2
2021-05-24
2021-05-15
2021-05-21
2021-05-22
2021-06-01
I would like to sum the columns "sales value" and "sales quantity" and create 4 more columns called "Period 1 Sales", "Period 1 Qty", "Period 2 Sales", and "Period 2 Qty".
For example, with the data above, I would get four new columns while rows would be grouped by product:
Product
Period 1 start
Period 1 end
Period 2 start
Period 2 end
Period 1 Sales Value
Period 1 Sales Qty
Period 2 Sales Value
Period 2 Sales Qty
XXX
2021-05-15
2021-05-21
2021-05-22
2021-06-01
6
2
9
3
YYY
2021-05-15
2021-05-21
2021-05-22
2021-06-01
0
0
10
3
I'm using SQL Server and right now I am pretty much stuck.
So far I managed to Cross Join my calendar table and sales table to get the table described in the first matrix.
Can anybody help?
Thanks a lot!
The following provides your desired results, with the exception of your value for YYY where in your sample data the values are in period1 not period 2.
select Product,
Period1start, Period1end, Period1start, Period2end,
Sum(P1sales) Period1Sales, Sum(P1Qty) Period1Qty,
Sum(P2sales) Period2Sales, Sum(P2Qty) Period2Qty
from t
cross apply(values(case when SalesDate between Period1start and Period1end then SalesValue else 0 end) )p1s(P1sales)
cross apply(values(case when SalesDate between Period1start and Period1end then SalesQty else 0 end) )p1q(P1Qty)
cross apply(values(case when SalesDate between Period2start and Period2end then SalesValue else 0 end) )p2s(P2sales)
cross apply(values(case when SalesDate between Period2start and Period2end then SalesQty else 0 end) )p2q(P2Qty)
group by product, Period1start, Period1end, Period1start, Period2end
See example DB<>Fiddle

Inventory Aging Report (FIFO)

Have a problem to solve in the SQL Server to generate a Inventory aging report using FIFO Based on SKU & Warehouse. I have attached the schema here.
SKU
TransactionType
WarehouseCode
TransactionDate
Qty
100
IN
WH1
2021-04-30
100
100
IN
WH2
2021-04-30
50
101
IN
WH1
2021-04-30
30
101
IN
WH2
2021-05-01
25
100
OUT
WH2
2021-05-02
30
100
OUT
WH1
2021-05-02
20
100
OUT
WH1
2021-05-04
50
100
OUT
WH2
2021-05-04
20
100
OUT
WH1
2021-05-05
25
100
IN
WH2
2021-05-10
30
100
IN
WH1
2021-05-11
30
101
OUT
WH2
2021-05-12
20
100
OUT
WH1
2021-05-15
30
Based on the above schema structure, i need to develop a inventory aging report based on first in first out (FIFO) and show the remaining qty of each SKU and Warehouse combination and make the previous incoming records remaining quantities as zero.
Expected report format Assuming the report is run on (2021-05-20)
SKU
TransactionType
WarehouseCode
TransactionDate
Qty
Remaining
Aging
100
IN
WH1
2021-04-30
100
0
21
100
IN
WH2
2021-04-30
50
0
21
101
IN
WH1
2021-04-30
30
30
21
101
IN
WH2
2021-05-01
25
5
20
100
IN
WH2
2021-05-10
30
30
11
100
IN
WH1
2021-05-11
50
5
10
Thanks in advance.
You can use window functions to sum up the IN records over time (a cumulative sum), and find out the final total OUT.
Then, if the in_so_far is less than the final_out, you know you have use everything for that line.
The aging just appears to be a datediff() against a constant value?
WITH
cumulative AS
(
SELECT
*,
SUM(CASE WHEN TransactionType = 'IN' THEN Qty ELSE 0 END)
OVER (
PARTITION BY SKU, WarehouseCode
ORDER BY TransactionDate
)
AS qty_in_so_far,
SUM(CASE WHEN TransactionType = 'OUT' THEN Qty ELSE 0 END)
OVER (
PARTITION BY SKU, WarehouseCode
)
AS qty_out_final
FROM
inventory
)
SELECT
*,
CASE
WHEN qty_out_final > qty_in_so_far
THEN 0
WHEN qty_in_so_far - qty_out_final > qty
THEN qty
ELSE qty_in_so_far - qty_out_final
END
AS qty_final,
DATEDIFF(day, TransactionDate, '2021-05-20') + 1 AS aging
FROM
cumulative
WHERE
TransactionType = 'IN'
ORDER BY
TransactionDate,
SKU,
WarehouseCode
Demo... https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=b6725edd625001cd874bdb8f37283763

Cumulated Cohorts in SQL

I have the following table :
cohort
month cohort
orders
cumulated orders
2021-01
0
126
126
2021-01
1
5
131
2021-01
2
4
135
2021-02
0
131
131
2021-02
1
9
140
2021-02
2
8
148
And now I want to have the following table where I divide each repeat orders by the number of orders of month 0 :
cohort
month cohort
orders
cumulated orders
cumulated in %
2021-01
0
126
126
100%
2021-01
1
5
131
104%
2021-01
2
4
135
107%
2021-02
0
131
131
100%
2021-02
1
9
140
107%
2021-02
2
8
148
114%
My only hint is to create a CASE statement, but I don't want each month to update the query by adding the line
WHEN cohort="2021-08" THEN cumulated orders / 143
where 143 is the number of orders of cohort 2021-08 at month cohort =0
Has someone got an idea how to get this table ?
A case expression isn't needed. You can use first_value():
select t.*,
( cumulated_order /
first_value(orders) over (partition by cohort order by month_cohort)
) as ratio
from t;
If you really wanted a case, you could use:
select t.*,
( cumulated_order /
max(case when month_cohort = 0 then orders end) over (partition by cohort)
) as ratio
from t;
Consider below
select *,
round(100 * cumulated_orders /
sum(if(month_cohort = 0, orders, 0)) over(partition by cohort)
) as cumulated_in_percent
from `project.dataset.table`
if applied to sample data in your question - output is

Select for take opening balance for last date of month and sum of other amount in whole month

I have table balance_detail
sales_period sales_date opening_amt sales_amt payment_amt closing_amt
-----------------------------------------------------------------------------
201501 01-01-2015 210 100 110
201501 02-01-2015 110 300 280 130
201501 03-01-2015 130 50 80
201501 05-01-2015 80 600 670 10
201502 02-02-2015 10 160 100 70
201502 15-02-2015 70 100 170 0
And Want result like this
sales_period opening_amt sales_amt payment_amt closing_amt
-----------------------------------------------------------------------------
201501 80 1110 1110 10
201502 70 260 270 0
One method is to use conditional aggregation along with row_number():
select sales_period,
max(case when seqnum = 1 then opening_amt end) as opening_amt,
sum(sales_amt) as sales_amt,
sum(payment_amt) as payment_amt,
max(case when seqnum = 1 then closing_amt end) as closing_amt
from (select bd.*,
row_number() over (partition by sales_period order sales_date desc) as seqnum
from balance_detail bd
) bd
group by sales_period;