Search code examples
sqlsql-serversql-server-2019

Creating a balance statement


So based on a start date of 5 years ago on the 07th February – I want to generate 5 rows - one per year from the start date. If the start date was 8 years ago - 8 rows.

Year StartDate EndDate
1 07-02-2020 06-02-2021
2 07-02-2021 06-02-2022
3 07-02-2022 06-02-2023
4 07-02-2023 06-02-2024
5 07-02-2024 06-02-2025

The following logic has to apply .

Ongoing Allowance – This is a £500 allowance that someone is given.

Customer Allowance – For Year 1 this will always be £500. Each year an additional £500 is added. Also need to take into account the Balance from the previous year. This should never exceed £1000. Also if by Year 3 they have not used their Allowance from Year 1 – it is removed. Same for Year 4 – the balance from year 2 would be removed.

Claimed – This is the amount from the Customer Allowance that they have actually spent.

Balance – Customer Allowance minus Claimed.

So are worked example is per below. It is the Actual Allowance and the Balance field I am having trouble calculating.

Year OngoingAllowance ActualAllowance Claimed Balance
1 500 500 100 400
2 500 900 0 900
3 500 1000 700 300
4 500 300 0 800
5 500 1000 0 1000

Year 1 – Actual Allowance starts at £500. This is always the starting point. £100 is claimed giving you a balance of £400.

Year 2 – Balance in year 1 of £400 PLUS the Ongoing Allowance of £500. Nothing is claimed that year – so the balance is £900 at the end of the year.

Year 3 – This is where is gets more complicated as Year 1 balance of £400 to be removed from Balance year 2 (900) as the balance is over two years old. So that gets you back to £500 PLUS the £500 for Year 3 Allowance - gives us an actual allowance of £1000. £700 is claimed – so the Balance at the end of year 3 is £300.

Year 4 – Balance at the end of Year 3 (£300) plus the £500 allowance for year 4 – nothing claimed. .. Balance at the end of year 4 = £800

Year 5. The Balance in Year 3 (£300) was not used so remove that from the balance at the end of year 4 = £500. Plus the balance for Year 5 of £500 = £1000 for the actual allowance.

I have some code per below – that created me 3 tables in order to store the Year, OngoingAllowance and Claimed. . . but I have no idea how to put into practice the Actual Allowance and Balance columns.

declare @StartDate datetime,  
        @EndDate datetime


select  @StartDate = DateAdd(yy, -5, GetDate()) , --This will be set to the Lease Start Date
        @EndDate = dateadd(year,1,DateAdd(yy, -5, GetDate())) -1  --Calculate the Lease End Date based on the Lease Start Date


--Create a table the holds up to 10 years worth of periods 

;with myCTE as  
   (  

             select 1 as 'Year' ,@StartDate 'PeriodStart' ,@EndDate 'PeriodEnd'
             union all  
             select  Year+1 ,dateadd(YEAR, 1, PeriodStart) ,  dateadd(YEAR, 1, PeriodEnd)

              FROM  myCTE  

              where Year+1 <= 5 --Only up to 5 years

    )  

select  Year
            ,Convert(varchar(10),PeriodStart,105)  as StartDate 
            ,Convert(varchar(10),PeriodEnd,105) as EndDate

        
        into #TmpPeriods
        from myCTE 


--Create Table to Hold Potential Allowance
create table #TmpAllowance ([Year] int, [Allowance] decimal (18,2))
INSERT INTO  #TmpAllowance (Year,Allowance)
values (1,500),(2,500),(3,500), (4,500),(5,500)

--Create Table to Hold Allowance Claimed
create table #TmpClaimed ([Year] int, [Claimed] decimal (18,2))
INSERT INTO  #TmpClaimed (Year,Claimed)
values (1,100),(2,0),(3,700), (4,0),(5,0)


select p.Year,p.StartDate, p.EndDate, a.Allowance 'AvailableAllowance', c.Claimed
from #TmpPeriods p
join #TmpAllowance a on a.Year = p.Year
join #TmpClaimed c on c.Year = a.Year

drop table #TmpPeriods
drop table #TmpAllowance
drop table #TmpClaimed

Solution

  • Becouse ActualAllowance should be calculated with some constraints, we can calculate ActualAllowance and Balance recursively, year by year, instead of cumulative sum (running total).

    Balance for previous year take from recursive part.

    See example:
    Source data as query

    select p.Year -- ,p.StartDate, p.EndDate
      , a.Allowance OngoingAllowance, c.Claimed
    from #TmpPeriods p
    join #TmpAllowance a on a.Year = p.Year
    join #TmpClaimed c on c.Year = a.Year
    ;
    
    Year OngoingAllowance Claimed
    1 500.00 100.00
    2 500.00 0.00
    3 500.00 700.00
    4 500.00 0.00
    5 500.00 0.00

    Recursive calculation of OnGoingAllowance, ActualAllowance and Balance

    with src as(
    select p.Year -- ,p.StartDate, p.EndDate
      , a.Allowance OngoingAllowance, c.Claimed
    from #TmpPeriods p
    join #TmpAllowance a on a.Year = p.Year
    join #TmpClaimed c on c.Year = a.Year
    )
    ,rCte as(
      select year,OnGoingAllowance
        ,OnGoingAllowance ActualAllowance
        ,Claimed
        ,cast(OnGoingAllowance-Claimed as decimal(18,2)) Balance
      from src t
      where year=1
      union all
      select t.year,t.OnGoingAllowance
        ,cast(
           case when (r.Balance+t.OnGoingAllowance)>1000 then 1000 
           else r.Balance+t.OnGoingAllowance
           end 
          as decimal(18,2)) ActualAllowance
        ,t.Claimed
        ,cast(
           case when (r.Balance+t.OnGoingAllowance)>1000 then 1000
           else r.Balance+t.OnGoingAllowance
           end -t.Claimed 
          as decimal(18,2)) Balance
      from rCte r inner join src t on t.year=(r.year+1)
    )
    select * from rCte
      ;
    
    year OnGoingAllowance ActualAllowance Claimed Balance
    1 500.00 500.00 100.00 400.00
    2 500.00 900.00 0.00 900.00
    3 500.00 1000.00 700.00 300.00
    4 500.00 800.00 0.00 800.00
    5 500.00 1000.00 0.00 1000.00

    fiddle