Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
SaibAnalytics
Frequent Visitor

Calculate cumulative loan balance due

Hello guys, 

 

I need help in coming up with a dax code for calculating cumulative oustanding loan amount per loan ID. I have monthly expected amount and first and last installment expected date. In the first row, i know the loan ref should have paid 135*2, Therefore oustanding due is 1500 - (135*2) . 

@amitchandak , @v-joesh-msft , @Greg_Deckler or Anyone who can help, i will really appreciate this!!!

 

 

Loan ReferenceLoan AmountInstallment AmountNumber of InstallmentsAllocation DataFirst Inst. DateLast Inst. DateLoan Installment daysCumulative Outstanding
Abx$1,500.00$135.001212/4/202220/05/202220/04/202330  =1500 - (135*2)
xyz$50,000.00$1,805.563627/08/202030/09/202031/08/202330As at today or Any date filtered
yds$1,500.00$270.00625/11/202130/12/202130/11/202260
1 ACCEPTED SOLUTION
daXtreme
Solution Sage
Solution Sage

// Assumptions:
// 1. Dates (your calendar) is not connected to the Loan
//    fact table.
// 2. After the last installment, the amount outstanding
//    is always 0.
// 3. Loan installment days can only be multiples of
//    30, meaning 1, 2, 3, 4... months.
// If the logic is different, you'll have to adjust the
// code.

[Outstanding] =
var LastDateSelected = MAX( Dates[Date] )
return
SUMX(
    Filter(
        Loans,
        // Other loans will for sure have their outstanding
        // balance of 0, so only these satisfying the below 
        // condition are of interest.
        and(
            Loans[First Inst. Date] <= LastDateSelected,
            LastDateSelected <= Loans[Last Inst. Date]
        )
    ),
    // For each loan we iterate, we need to figure out
    // how many installments have already been paid.
    // Bear in mind that if LastDateSelected is a day
    // on which an installment should be paid, the installment
    // is treated as having been paid.
    var FirstInstallmentDay = Loans[First Inst. Date]
    var FirstInstallmentDayInCalendar =
        CALCULATETABLE(
            DISTINCT( Dates[Date] ),
            Dates[Date] = FirstInstallmentDay
        )
    var NumOfInstallments = Loans[Number of installments]
    // Granularity tells you the number of months between
    // consecutive payments.
    var Granularity = int( Loans[Loan Installment Days] / 30 )
    var AllPaymentDays =
        GENERATE(
            // -1 because we start from 0
            GENERATESERIES(0, NumOfinstallments - 1, 1),
            var InstallmentNumber = [Value]
            var PaymentDay =
                DATEADD(
                    FirstInstallmentDayInCalendar,
                    Granularity * InstallmentNumber,
                    MONTH
                )
            return
                ROW( "@PaymentDay", PaymentDay )
        )
    var PaymentDaysBeforeOrOnLastDateSelected =
        FILTER(
            AllPaymentDays,
            [@PaymentDay] <= LastDateSelected
        )
    var NumOfInstallmentsPaid =
        COUNTROWS( PaymentDaysBeforeOrOnLastDateSelected )
    var InstallmentAmount = Loans[Installment Amount]
    var AmountPaid = InstallmentAmount * NumOfInstallmentsPaid
    var LoanAmount = Loans[Loan Amount]
    // Using MAX just in case there are any rounding errors...
    var Outstanding = MAX( LoanAmount - AmountPaid, 0 )
    return
        // If Outstanding is 0, BLANK is returned.
        If( Outstanding, Outstanding )
)

View solution in original post

7 REPLIES 7
SaibAnalytics
Frequent Visitor

Hi @daXtreme, Again, thanks alot!

I have used the Dax, it does not have an error but i am unable to see the results when I put the measure on a visual. i get the error, "

Error Message: MdxScript(Model) (110, 38) Calculation error in measure 'Dax Measures'[Outstanding]: A table of multiple values was supplied where a single value was expected.
I should also mention, I have a calender table that has an active relationship with allocation date and an incative relationship withe [First Inst. Date] and [Last Inst. Date]. I tried editing the code as below.

Outstanding =
// Assumptions:
// 1. Dates (your calendar) is not connected to the Loan fact table.
// 2. After the last installment, the amount outstanding is always 0.
// 3. Loan installment days can only be multiples of 30, meaning 1, 2, 3, 4... months.
// If the logic is different, you'll have to adjust the code.

var LastDateSelected = MAX( calendarTable[Date])
var firstInstallmentDate = CALCULATE(All(Loans[First Inst. Date]),USERELATIONSHIP(Loans[First Inst. Date],calendarTable[Date]))
var lastInstallmentDate = CALCULATE(All(Loans[Last Inst. Date]),USERELATIONSHIP(Loans[Last Inst. Date],calendarTable[Date]))

return
SUMX(
Filter(
Loans,
// Other loans will for sure have their outstanding
// balance of 0, so only these satisfying the below
// condition are of interest.
and(
firstInstallmentDate <= LastDateSelected,
LastDateSelected <= lastInstallmentDate
)
),
// For each loan we iterate, we need to figure out
// how many installments have already been paid.
// Bear in mind that if LastDateSelected is a day
// on which an installment should be paid, the installment
// is treated as having been paid.

var FirstInstallmentDay = firstInstallmentDate
var FirstInstallmentDayInCalendar =
CALCULATETABLE(
DISTINCT( calendarTable[Date]),
calendarTable[Date] = FirstInstallmentDay
)
var NumOfInstallments = Loans[Number Installment ]
// Granularity tells you the number of months between
// consecutive payments.
var Granularity = int(Loans[Loan Installment Days] / 30 )
var AllPaymentDays =
GENERATE(
// -1 because we start from 0
GENERATESERIES(0, NumOfinstallments - 1, 1),
var InstallmentNumber = [Value]
var PaymentDay =
DATEADD(
FirstInstallmentDayInCalendar,
Granularity * InstallmentNumber,
MONTH
)
return
ROW( "@PaymentDay", PaymentDay )
)
var PaymentDaysBeforeOrOnLastDateSelected =
FILTER(
AllPaymentDays,
[@PaymentDay] <= LastDateSelected
)
var NumOfInstallmentsPaid =
COUNTROWS( PaymentDaysBeforeOrOnLastDateSelected )
var InstallmentAmount = Loans[Installment Amount]
var AmountPaid = InstallmentAmount * NumOfInstallmentsPaid
var LoanAmount = Loans[Price of Loan]
// Using MAX just in case there are any rounding errors...
var Outstanding = MAX( LoanAmount - AmountPaid, 0 )
return
// If Outstanding is 0, BLANK is returned.
If( Outstanding, Outstanding )
)




@SaibAnalytics 

 

I can't help you with the error because I don't know which place it's attached to. It's a rather standard error that you should be able to figure out yourself when debugging the measure.

daXtreme
Solution Sage
Solution Sage

// Assumptions:
// 1. Dates (your calendar) is not connected to the Loan
//    fact table.
// 2. After the last installment, the amount outstanding
//    is always 0.
// 3. Loan installment days can only be multiples of
//    30, meaning 1, 2, 3, 4... months.
// If the logic is different, you'll have to adjust the
// code.

[Outstanding] =
var LastDateSelected = MAX( Dates[Date] )
return
SUMX(
    Filter(
        Loans,
        // Other loans will for sure have their outstanding
        // balance of 0, so only these satisfying the below 
        // condition are of interest.
        and(
            Loans[First Inst. Date] <= LastDateSelected,
            LastDateSelected <= Loans[Last Inst. Date]
        )
    ),
    // For each loan we iterate, we need to figure out
    // how many installments have already been paid.
    // Bear in mind that if LastDateSelected is a day
    // on which an installment should be paid, the installment
    // is treated as having been paid.
    var FirstInstallmentDay = Loans[First Inst. Date]
    var FirstInstallmentDayInCalendar =
        CALCULATETABLE(
            DISTINCT( Dates[Date] ),
            Dates[Date] = FirstInstallmentDay
        )
    var NumOfInstallments = Loans[Number of installments]
    // Granularity tells you the number of months between
    // consecutive payments.
    var Granularity = int( Loans[Loan Installment Days] / 30 )
    var AllPaymentDays =
        GENERATE(
            // -1 because we start from 0
            GENERATESERIES(0, NumOfinstallments - 1, 1),
            var InstallmentNumber = [Value]
            var PaymentDay =
                DATEADD(
                    FirstInstallmentDayInCalendar,
                    Granularity * InstallmentNumber,
                    MONTH
                )
            return
                ROW( "@PaymentDay", PaymentDay )
        )
    var PaymentDaysBeforeOrOnLastDateSelected =
        FILTER(
            AllPaymentDays,
            [@PaymentDay] <= LastDateSelected
        )
    var NumOfInstallmentsPaid =
        COUNTROWS( PaymentDaysBeforeOrOnLastDateSelected )
    var InstallmentAmount = Loans[Installment Amount]
    var AmountPaid = InstallmentAmount * NumOfInstallmentsPaid
    var LoanAmount = Loans[Loan Amount]
    // Using MAX just in case there are any rounding errors...
    var Outstanding = MAX( LoanAmount - AmountPaid, 0 )
    return
        // If Outstanding is 0, BLANK is returned.
        If( Outstanding, Outstanding )
)

Thanks a lot for this! let me try the solution.

Hi there.

 

I've been looking at the file. Loan "aa", for instance, has 12 installments @ 135 each. The borrowed amount is 1500. Since 12 * 135 = 1620 =/= 1500, this violates one of the assumptions which states that after the last installment the amount outstanding should be 0. That's one thing I've noticed.

 

Secondly, the formula seems to work OK but you cannot, as I said, have an active relationship between the time axis and the Loans table. This is because the time axis must show, for any selected loan(!), its balances as the time progresses. This therefore must be an independent axis.

 

If you want to be able to slice loans by one of the 3 dates that you store in the Loans table, then you have 2 choices:

  1. Have 2 date tables (with different and meaningful names). One will be the independent one on which you can show how the balances progress and the other one will be used for slicing the loans by ONE of the date fields (most likely Allocation Date).
  2. Have one date table (the independent time axis) and just expose the dates from the Loans table as attributes of the loans. You'll then be able to slice by them.

Point 2) is the right setup you should have. Then the measure will work as expected.

 

Best

Thanks a lot! I appreciate the help.
I have changed my data and used used option 2 and it worked!

Glad it's worked.

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.

Top Solution Authors