UNPKG

@wealthx/borrow-capacity-lib

Version:
651 lines (642 loc) 30.3 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); (function (AccountType) { AccountType["Transaction"] = "transaction"; AccountType["Savings"] = "savings"; AccountType["CreditCard"] = "credit-card"; AccountType["Mortgage"] = "mortgage"; AccountType["Loan"] = "loan"; AccountType["Investment"] = "investment"; AccountType["Share"] = "share"; AccountType["TermDeposit"] = "term-deposit"; AccountType["Insurance"] = "insurance"; AccountType["Other"] = "other"; AccountType["Unknown"] = "unknown"; AccountType["Superannuation"] = "superannuation"; })(exports.AccountType || (exports.AccountType = {})); (function (PeriodType) { PeriodType["Monthly"] = "monthly"; PeriodType["Weekly"] = "weekly"; })(exports.PeriodType || (exports.PeriodType = {})); function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } /** * Process scenario finance history to calculate aggregated monthly data * This function processes expense and income data for all participants across multiple months * * @param scenarioFinanceHistory - Array of scenario finance history items * @param scenarioFinanceHistory.incomeAndExpensesSummary.avgExpenseByLastXMonths - Negative value represent a deduction, Example: { "2025-09": -1000 } * @param scenarioFinanceHistory.incomeAndExpensesSummary.avgRepaymentExpenseByLastXMonths - Positive value represent a repayment, Example: { "2025-09": 1000 } * @param scenarioFinanceHistory.incomeAndExpensesSummary.incomesByLastXMonths - Positive value represent a credit action, Example: { "2025-09": { "regular": [{ "avgAmount": 1000, "isRentalIncome": true }] } } * @param userId - Main applicant user ID * @returns Array of processed history data sorted by month */ function processScenarioHistoryData(scenarioFinanceHistory, userId) { var allMonthsData = {}; // Process each scenario's participant finance history (scenarioFinanceHistory || []).forEach(function (item) { var isMainApplicant = item.memberId === userId; var summary = item == null ? void 0 : item.incomeAndExpensesSummary; if (!summary) return; // Process expense data for all months var expenseByMonths = summary.avgExpenseByLastXMonths || {}; var repaymentExpenseByMonths = summary.avgRepaymentExpenseByLastXMonths || {}; Object.keys(expenseByMonths).forEach(function (monthKey) { if (!allMonthsData[monthKey]) { allMonthsData[monthKey] = { month: monthKey, mainApplicantMonthlyIncome: 0, coApplicantMonthlyIncome: 0, mainApplicantMonthlyRentalIncome: 0, coApplicantMonthlyRentalIncome: 0, monthlyExpense: 0, monthlyDebtRepayment: 0 }; } var monthNonRepaymentExpenseValues = Math.abs(Number(expenseByMonths[monthKey]) || 0); // Repayment transaction amount is positive (user add credit to loan account => positive amount) var monthDebtRepaymentValues = Number(repaymentExpenseByMonths[monthKey]) || 0; allMonthsData[monthKey].monthlyExpense += monthNonRepaymentExpenseValues; allMonthsData[monthKey].monthlyDebtRepayment += monthDebtRepaymentValues; }); // Process income data for all months var incomesByMonths = summary.incomesByLastXMonths || {}; Object.keys(incomesByMonths).forEach(function (monthKey) { var _incomesByMonths$mont; if (!allMonthsData[monthKey]) { allMonthsData[monthKey] = { month: monthKey, mainApplicantMonthlyIncome: 0, coApplicantMonthlyIncome: 0, mainApplicantMonthlyRentalIncome: 0, coApplicantMonthlyRentalIncome: 0, monthlyExpense: 0, monthlyDebtRepayment: 0 }; } var monthIncomeValues = ((_incomesByMonths$mont = incomesByMonths[monthKey]) == null ? void 0 : _incomesByMonths$mont.regular) || []; var incomeData = monthIncomeValues.reduce(function (applicantAcc, curr) { var updatedAcc = _extends({}, applicantAcc); var fieldToUpdate; if (curr.isRentalIncome) { fieldToUpdate = isMainApplicant ? "mainApplicantMonthlyRentalIncome" : "coApplicantMonthlyRentalIncome"; } else { fieldToUpdate = isMainApplicant ? "mainApplicantMonthlyIncome" : "coApplicantMonthlyIncome"; } updatedAcc[fieldToUpdate] += Number(curr.avgAmount) || 0; return updatedAcc; }, { mainApplicantMonthlyIncome: 0, coApplicantMonthlyIncome: 0, mainApplicantMonthlyRentalIncome: 0, coApplicantMonthlyRentalIncome: 0 }); allMonthsData[monthKey].mainApplicantMonthlyIncome += incomeData.mainApplicantMonthlyIncome; allMonthsData[monthKey].coApplicantMonthlyIncome += incomeData.coApplicantMonthlyIncome; allMonthsData[monthKey].mainApplicantMonthlyRentalIncome += incomeData.mainApplicantMonthlyRentalIncome; allMonthsData[monthKey].coApplicantMonthlyRentalIncome += incomeData.coApplicantMonthlyRentalIncome; }); }); // Convert to array and round values var historyData = Object.values(allMonthsData).map(function (monthData) { return _extends({}, monthData, { mainApplicantMonthlyIncome: Math.round(monthData.mainApplicantMonthlyIncome), coApplicantMonthlyIncome: Math.round(monthData.coApplicantMonthlyIncome), mainApplicantMonthlyRentalIncome: Math.round(monthData.mainApplicantMonthlyRentalIncome), coApplicantMonthlyRentalIncome: Math.round(monthData.coApplicantMonthlyRentalIncome), monthlyExpense: Math.round(monthData.monthlyExpense), monthlyDebtRepayment: Math.round(monthData.monthlyDebtRepayment) }); }).sort(function (a, b) { return a.month.localeCompare(b.month); }); return historyData; } var DEFAULT_INTEREST_RATE = 0.0524; var DEFAULT_SURPLUS_AVAILABLE_FOR_LOAN_RATE = 1; var DEFAULT_LOAN_TERM = 30 * 12; var DEFAULT_ASSESSMENT_BUFFER_RATE = 0.03; var DEFAULT_HEM_BUFFER_RATE = 0.05; var DEFAULT_SHADED_RENTAL_INCOME_RATIO = 0.8; var accountTypes = /*#__PURE__*/Object.values(exports.AccountType); /** * Credit card, mortgage, loan */ var liabilityAccountTypes = [exports.AccountType.CreditCard, exports.AccountType.Mortgage, exports.AccountType.Loan]; /** * Loan, mortgage */ var loanAccountTypes = [exports.AccountType.Loan, exports.AccountType.Mortgage]; /** * Not credit card, mortgage, loan */ var nonLiabilityAccountTypes = /*#__PURE__*/accountTypes.filter(function (type) { return !liabilityAccountTypes.includes(type); }); var nonSuperannuationAccountTypes = /*#__PURE__*/accountTypes.filter(function (type) { return type !== exports.AccountType.Superannuation; }); // Income bracket upper bounds for lookup var HEM_INCOME_BRACKETS = [26000, 39000, 52000, 66000, 79000, 105000, 131000, 157000, 184000, 210000, 262000, 328000, 394000, 656000]; // HEM (Household Expenditure Measure) data structure // Map<PeriodType, Map<number of applicant, Map<number of dependants, money[]>>> var HEM_DATA = /*#__PURE__*/new Map([[exports.PeriodType.Weekly, /*#__PURE__*/new Map([[2, /*#__PURE__*/new Map([ // Couple households [0, [599, 599, 613, 638, 679, 742, 829, 903, 985, 1034, 1100, 1216, 1389, 1403]], [1, [599, 599, 704, 728, 769, 833, 920, 995, 1077, 1126, 1192, 1309, 1482, 1497]], [2, [599, 599, 769, 794, 835, 897, 984, 1058, 1140, 1189, 1255, 1371, 1543, 1558]], [3, [599, 599, 769, 859, 900, 962, 1049, 1123, 1205, 1254, 1319, 1436, 1607, 1622]]])], [1, /*#__PURE__*/new Map([ // Single households [0, [311, 326, 341, 366, 407, 470, 557, 631, 714, 762, 828, 945, 1118, 1133]], [1, [311, 425, 440, 464, 505, 567, 654, 728, 810, 858, 924, 1040, 1211, 1226]], [2, [311, 536, 551, 575, 616, 678, 765, 839, 920, 969, 1034, 1150, 1321, 1336]], [3, [311, 536, 662, 687, 727, 789, 876, 949, 1031, 1079, 1145, 1260, 1431, 1446]]])]])], [exports.PeriodType.Monthly, /*#__PURE__*/new Map([[2, /*#__PURE__*/new Map([ // Couple households [0, [2394, 2394, 2453, 2553, 2715, 2967, 3316, 3612, 3942, 4135, 4399, 4865, 5555, 5614]], [1, [2394, 2394, 2814, 2914, 3077, 3330, 3681, 3978, 4309, 4503, 4768, 5236, 5929, 5988]], [2, [2394, 2394, 3077, 3176, 3338, 3590, 3938, 4233, 4562, 4755, 5018, 5483, 6171, 6230]], [3, [2394, 2394, 3077, 3436, 3598, 3849, 4197, 4493, 4821, 5014, 5277, 5742, 6430, 6489]]])], [1, /*#__PURE__*/new Map([ // Single households [0, [1244, 1305, 1364, 1464, 1627, 1879, 2229, 2525, 2855, 3049, 3313, 3780, 4471, 4530]], [1, [1244, 1700, 1758, 1857, 2019, 2270, 2617, 2912, 3239, 3432, 3695, 4158, 4844, 4903]], [2, [1244, 2144, 2203, 2302, 2463, 2714, 3061, 3355, 3682, 3874, 4136, 4600, 5284, 5343]], [3, [1244, 2144, 2648, 2746, 2908, 3158, 3504, 3798, 4124, 4316, 4578, 5041, 5725, 5784]]])]])]]); // Australian Tax Rates for 2025-26 (for reference) var AUSTRALIAN_TAX_BRACKETS = [{ min: 0, max: 18200, rate: 0.0, baseTax: 0 }, { min: 18201, max: 45000, rate: 0.16, baseTax: 0 }, { min: 45001, max: 135000, rate: 0.3, baseTax: 4288 }, { min: 135001, max: 190000, rate: 0.37, baseTax: 31288 }, { min: 190001, max: Number.MAX_SAFE_INTEGER, rate: 0.45, baseTax: 51638 }]; // Medicare Levy rate (2% on entire taxable income) var MEDICARE_LEVY_RATE = 0.02; var getReverseBrackets = function getReverseBrackets(includeMedicareLevy) { var brackets = []; for (var _i = 0, _AUSTRALIAN_TAX_BRACK = AUSTRALIAN_TAX_BRACKETS; _i < _AUSTRALIAN_TAX_BRACK.length; _i++) { var _AUSTRALIAN_TAX_BRACK2 = _AUSTRALIAN_TAX_BRACK[_i], min = _AUSTRALIAN_TAX_BRACK2.min, max = _AUSTRALIAN_TAX_BRACK2.max, rate = _AUSTRALIAN_TAX_BRACK2.rate, baseTax = _AUSTRALIAN_TAX_BRACK2.baseTax; var gRatio = 1 - rate - (includeMedicareLevy ? MEDICARE_LEVY_RATE : 0); var base = (min - 1) * rate - baseTax; var minValue = Math.round(min * gRatio + base); var maxValue = max === Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : Math.round(max * gRatio + base); brackets.push({ min: minValue, max: maxValue, gRatio: gRatio, base: base }); } return brackets; }; var AUSTRALIAN_REVERSE_TAX_BRACKETS = /*#__PURE__*/getReverseBrackets(true); var AUSTRALIAN_REVERSE_TAX_BRACKETS_WITHOUT_MEDICARE_LEVY = /*#__PURE__*/getReverseBrackets(false); /** * Calculate borrowing capacity based on income, expenses, and interest rate * Formula based on: https://wealthx.atlassian.net/wiki/spaces/WD/pages/86343838/Finance+Borrow+capacity+formula * * @param params.monthlyIncome - NET Monthly income not including rental income * @param params.monthlyExpense - NET Monthly expense * @param params.interestRate - Annual interest rate (as decimal, e.g., 0.05 for 5%) * @param params.bufferRate - Buffer of assessment rate (default 0.03) * @param params.surplusAvailableForLoanRate - Actual portion of surplus available for loan (default 1.0) * @param params.loanTerm - Default 30 years (360 months) * @param params.numberOfApplicants - Number of applicants (default 1 for single, 2 for couple) * @param params.numberOfDependants - Number of dependants (default 0) * @param params.monthlyRentalIncome - NET Monthly rental income * @returns Maximum borrowing capacity */ function calculateBorrowCapacity(params) { var _params$monthlyIncome = params.monthlyIncome, monthlyIncome = _params$monthlyIncome === void 0 ? 0 : _params$monthlyIncome, _params$monthlyExpens = params.monthlyExpense, monthlyExpense = _params$monthlyExpens === void 0 ? 0 : _params$monthlyExpens, _params$interestRate = params.interestRate, interestRate = _params$interestRate === void 0 ? DEFAULT_INTEREST_RATE : _params$interestRate, _params$loanTerm = params.loanTerm, loanTerm = _params$loanTerm === void 0 ? DEFAULT_LOAN_TERM : _params$loanTerm, _params$bufferRate = params.bufferRate, bufferRate = _params$bufferRate === void 0 ? DEFAULT_ASSESSMENT_BUFFER_RATE : _params$bufferRate, _params$surplusAvaila = params.surplusAvailableForLoanRate, surplusAvailableForLoanRate = _params$surplusAvaila === void 0 ? DEFAULT_SURPLUS_AVAILABLE_FOR_LOAN_RATE : _params$surplusAvaila, _params$numberOfAppli = params.numberOfApplicants, numberOfApplicants = _params$numberOfAppli === void 0 ? 1 : _params$numberOfAppli, _params$numberOfDepen = params.numberOfDependants, numberOfDependants = _params$numberOfDepen === void 0 ? 0 : _params$numberOfDepen, _params$monthlyRental = params.monthlyRentalIncome, monthlyRentalIncome = _params$monthlyRental === void 0 ? 0 : _params$monthlyRental; var originalAnnualTotalIncome = (monthlyIncome + monthlyRentalIncome) * 12; var grossAnnualTotalIncome = netToGross({ netIncome: originalAnnualTotalIncome, includeMedicareLevy: true }); var hemExpense = calculateHEMExpense({ annualIncome: grossAnnualTotalIncome, numberOfApplicants: numberOfApplicants, numberOfDependants: numberOfDependants, period: exports.PeriodType.Monthly }); var expenseForSurplusCalculation = monthlyExpense < hemExpense ? hemExpense : monthlyExpense; // Calculate surplus available for loan (100% of net income) var shadedRentalIncome = monthlyRentalIncome * DEFAULT_SHADED_RENTAL_INCOME_RATIO; var monthlyTotalIncomeForSurplusCalculation = monthlyIncome + shadedRentalIncome; var surplusForLoan = Math.max(monthlyTotalIncomeForSurplusCalculation - expenseForSurplusCalculation, 0) * surplusAvailableForLoanRate; // Assessment rate includes a 3% buffer var assessmentRate = (interestRate + bufferRate) / 12; if (assessmentRate === 0) { // If no interest rate, simple calculation return surplusForLoan * loanTerm; } // Present value of annuity formula var borrowCapacity = surplusForLoan * (Math.pow(1 + assessmentRate, loanTerm) - 1) / (assessmentRate * Math.pow(1 + assessmentRate, loanTerm)); return borrowCapacity; } /** * Calculate monthly repayment amount for a loan * Formula based on: https://wealthx.atlassian.net/wiki/spaces/WD/pages/86343838/Finance+Borrow+capacity+formula * * @param params.interestRate - Annual interest rate (as decimal, e.g., 0.05 for 5%) * @param params.loanTerm - Loan term in months (default 30 years) * @returns Monthly repayment amount */ function calculateLoanRepayment(params) { var principal = params.principal, _params$interestRate2 = params.interestRate, interestRate = _params$interestRate2 === void 0 ? DEFAULT_INTEREST_RATE : _params$interestRate2, _params$loanTerm2 = params.loanTerm, loanTerm = _params$loanTerm2 === void 0 ? DEFAULT_LOAN_TERM : _params$loanTerm2; var monthlyRate = interestRate / 12; if (monthlyRate === 0) { // If no interest rate, simple division return principal / loanTerm; } // Monthly payment formula for fixed-rate loan var monthlyPayment = principal * (monthlyRate * Math.pow(1 + monthlyRate, loanTerm)) / (Math.pow(1 + monthlyRate, loanTerm) - 1); return monthlyPayment; } /** * Calculate HEM (Household Expenditure Measure) expense based on income, household composition, and period * * @param params.totalIncome - GROSS Total household ANNUAL income * @param params.numberOfApplicants - Number of applicants (2 for couple, 1 for single) * @param params.numberOfDependants - Number of dependants (0-3, capped at 3) * @param params.period - Period type (weekly or monthly) * @param params.bufferRate - Buffer rate to add on top (default 0.05 for 5%) * @returns HEM expense amount */ function calculateHEMExpense(params) { var annualIncome = params.annualIncome, numberOfApplicants = params.numberOfApplicants, numberOfDependants = params.numberOfDependants, period = params.period, _params$bufferRate2 = params.bufferRate, bufferRate = _params$bufferRate2 === void 0 ? DEFAULT_HEM_BUFFER_RATE : _params$bufferRate2; // Cap dependants at 3 var cappedDependants = Math.min(Math.max(numberOfDependants, 0), 3); // Cap income at 656000 var cappedIncome = Math.min(Math.max(annualIncome, 0), HEM_INCOME_BRACKETS[HEM_INCOME_BRACKETS.length - 1]); // Get the data for the specified period var periodData = HEM_DATA.get(period); if (!periodData) { throw new Error("Invalid period type: " + period); } // Get the data for the number of applicants var applicantData = periodData.get(numberOfApplicants); if (!applicantData) { throw new Error("Invalid number of applicants: " + numberOfApplicants + ". Must be 1 (single) or 2 (couple)"); } // Get the data for the number of dependants var dependantData = applicantData.get(cappedDependants); if (!dependantData) { throw new Error("Invalid number of dependants: " + cappedDependants); } // Find the appropriate income bracket using binary search var left = 0; var right = HEM_INCOME_BRACKETS.length - 1; var bracketIndex = -1; while (left <= right) { var mid = Math.floor((left + right) / 2); if (cappedIncome <= HEM_INCOME_BRACKETS[mid]) { bracketIndex = mid; right = mid - 1; // Look for the first occurrence } else { left = mid + 1; } } if (bracketIndex === -1) { throw new Error("Something went wrong"); } // Get the expense value for the income bracket var expenseValue = dependantData[bracketIndex]; if (expenseValue === undefined) { throw new Error("No HEM data available for income " + cappedIncome + " with " + numberOfApplicants + " applicants and " + cappedDependants + " dependants"); } // Apply the 5% buffer rate return expenseValue * (1 + bufferRate); } /** * Calculate tax payable on gross income using Australian tax brackets * Based on 2025-26 Australian Resident Tax Rates * @param grossIncome - Annual gross income * @returns Tax payable amount */ function calculateTaxPayable(grossIncome) { var _grossIncome = Math.floor(grossIncome); if (_grossIncome <= 0) return 0; var braket = AUSTRALIAN_TAX_BRACKETS.find(function (bracket) { return _grossIncome >= bracket.min && _grossIncome <= bracket.max; }); if (!braket) return 0; return braket.baseTax + (_grossIncome - braket.min) * braket.rate; } /** * @param params.grossIncome - Annual gross income * @param params.includeMedicareLevy - Whether to include 2% Medicare levy (default: true) * @returns Net income after tax and Medicare levy */ function grossToNet(params) { var grossIncome = params.grossIncome, _params$includeMedica = params.includeMedicareLevy, includeMedicareLevy = _params$includeMedica === void 0 ? true : _params$includeMedica; if (grossIncome <= 0) return 0; var taxPayable = calculateTaxPayable(grossIncome); var medicareLevy = includeMedicareLevy ? grossIncome * MEDICARE_LEVY_RATE : 0; return grossIncome - taxPayable - medicareLevy; } /** * Convert net income to gross income (reverse calculation) * @param params.netIncome - Target net income after tax and Medicare levy * @param params.includeMedicareLevy - Whether to include 2% Medicare levy (default: true) * @returns Gross income required to achieve the target net income */ function netToGross(params) { var netIncome = params.netIncome, _params$includeMedica2 = params.includeMedicareLevy, includeMedicareLevy = _params$includeMedica2 === void 0 ? true : _params$includeMedica2; var brackets = includeMedicareLevy ? AUSTRALIAN_REVERSE_TAX_BRACKETS : AUSTRALIAN_REVERSE_TAX_BRACKETS_WITHOUT_MEDICARE_LEVY; var bracket = brackets.find(function (bracket) { return netIncome >= bracket.min && netIncome <= bracket.max; }); if (!bracket) return 0; return Math.round((netIncome - bracket.base) / bracket.gRatio); } function calculateCashAvailable(accounts) { var filteredAccounts = accounts == null ? void 0 : accounts.filter(function (account) { var _account$class; var accountType = ((_account$class = account["class"]) == null ? void 0 : _account$class.type) || exports.AccountType.Unknown; return accountType !== exports.AccountType.Superannuation && nonLiabilityAccountTypes.includes(accountType); }); return filteredAccounts == null ? void 0 : filteredAccounts.reduce(function (totalBalance, _ref) { var _ref$balance = _ref.balance, balance = _ref$balance === void 0 ? 0 : _ref$balance; return totalBalance + (balance > 0 ? balance : 0); }, 0); } function calculateLiability(accounts) { var filteredLiabilityAccount = accounts == null ? void 0 : accounts.filter(function (account) { var _account$class2; return liabilityAccountTypes.includes((account == null || (_account$class2 = account["class"]) == null ? void 0 : _account$class2.type) || exports.AccountType.Unknown); }); return filteredLiabilityAccount == null ? void 0 : filteredLiabilityAccount.reduce(function (sum, _ref2) { var _ref2$balance = _ref2.balance, balance = _ref2$balance === void 0 ? 0 : _ref2$balance; return sum + (balance < 0 ? balance : 0); }, 0); } /** * Calculate Loan-to-Value Ratio (LVR) as a percentage * LVR = (Mortgage Loan / Properties Estimated Value) * 100 * * @param params.mortgageLoan - Total mortgage loan amount (positive number) * @param params.propertiesEstimatedValue - Total estimated value of properties * @returns LVR as percentage (0-100), returns 0 if no properties, 100 if mortgage >= property value */ function calculateLVR(params) { var mortgageLoan = params.mortgageLoan, propertiesEstimatedValue = params.propertiesEstimatedValue; if (propertiesEstimatedValue <= 0) { return 0; } if (mortgageLoan >= propertiesEstimatedValue) { return 100; } return mortgageLoan / propertiesEstimatedValue * 100 || 0; } /** * Calculate property equity amount * Equity = Properties Estimated Value - Mortgage Loan Amount * * @param params.propertiesEstimatedValue - Total estimated value of properties * @param params.mortgageLoan - Total mortgage loan amount (positive number) * @returns Equity amount (minimum 0) */ function calculateEquity(params) { var propertiesEstimatedValue = params.propertiesEstimatedValue, mortgageLoan = params.mortgageLoan; return Math.max(propertiesEstimatedValue - mortgageLoan, 0); } /** * Calculate excess monthly surplus * Excess Monthly Surplus = max(Total Income + Total Expense, 0) * Note: totalExpense is expected to be negative in the backend * * @param params.totalIncome - Total monthly income (positive) * @param params.totalExpense - Total monthly expense (negative value) * @returns Excess monthly surplus (minimum 0) */ function calculateExcessMonthlySurplus(params) { var totalIncome = params.totalIncome, totalExpense = params.totalExpense; // totalExpense is negative, so we use + to subtract return Math.max(totalIncome + totalExpense, 0); } /** * Calculate total estimated value of properties * * @param params.properties - Array of properties with estimate values * @returns Total estimated value of all properties */ function calculatePropertiesEstimatedValue(params) { var properties = params.properties; return properties.reduce(function (acc, property) { return acc + ((property == null ? void 0 : property.estimate) || 0); }, 0); } /** * Calculate total mortgage loan amount from accounts linked to properties * * @param params.accounts - Array of accounts with balance and type information * @param params.propertyLinkedAccountIds - Set of account IDs linked to properties * @returns Total mortgage loan amount (positive number) */ function calculateMortgageLoan(params) { var accounts = params.accounts, propertyLinkedAccountIds = params.propertyLinkedAccountIds; var totalMortgageLoan = accounts.filter(function (account) { return propertyLinkedAccountIds.has(account.id); }).reduce(function (acc, account) { var newLocal = Number(account.balance); // Only take negative balance (loan amount) return acc + (newLocal < 0 ? newLocal : 0); }, 0); // Return absolute value as mortgage loan amount should be positive return Math.abs(totalMortgageLoan); } /** * Extract lender information from mortgage accounts * * @param accounts - Array of account data * @returns Object containing lender names, short names, and interest rates */ function getLenderInfo(accounts) { var mortgageAccounts = accounts.filter(function (account) { var _account$class3; return ((_account$class3 = account["class"]) == null ? void 0 : _account$class3.type) === exports.AccountType.Mortgage; }); return mortgageAccounts.reduce(function (acc, account) { var _account$meta; acc.lenderNames.push(account.institutionName || "Unknown"); acc.lenderShortNames.push(account.institutionShortName || null); var rawLendingRate = account.lendingRate || ((_account$meta = account.meta) == null || (_account$meta = _account$meta.lendingRates) == null || (_account$meta = _account$meta[0]) == null ? void 0 : _account$meta.rate); acc.debtInterestRates.push(rawLendingRate ? parseFloat(rawLendingRate) : null); return acc; }, { lenderNames: [], lenderShortNames: [], debtInterestRates: [] }); } /** * Get linked property account IDs from properties * * @param properties - Array of properties with accountIds * @returns Set of account IDs linked to properties */ function getLinkedPropertyAccountIds(properties) { return properties.reduce(function (acc, property) { var _property$accountIds; var accountIds = ((_property$accountIds = property.accountIds) == null ? void 0 : _property$accountIds.split(",")) || []; accountIds.forEach(function (accountId) { if (accountId.trim()) { acc.add(accountId.trim()); } }); return acc; }, new Set()); } /** * Calculate comprehensive finance summary including equity, properties value, and mortgage loan * This combines property data with account data to calculate key financial metrics * * @param params.properties - Array of properties with estimates and linked account IDs * @param params.accounts - Array of accounts with balances and types * @returns Object containing equity, properties estimated value, and mortgage loan amount */ function calculateEquityData(params) { var properties = params.properties, accounts = params.accounts; // Calculate total estimated value of properties var propertiesEstimatedValue = calculatePropertiesEstimatedValue({ properties: properties }); // Get account IDs linked to properties var propertyLinkedAccountIds = getLinkedPropertyAccountIds(properties); // Calculate mortgage loan amount from linked accounts var mortgageLoan = calculateMortgageLoan({ accounts: accounts, propertyLinkedAccountIds: propertyLinkedAccountIds }); // Calculate equity var equity = calculateEquity({ propertiesEstimatedValue: propertiesEstimatedValue, mortgageLoan: mortgageLoan }); return { equity: equity, propertiesEstimatedValue: propertiesEstimatedValue, mortgageLoan: mortgageLoan }; } /** * Calculate buying goal * Buying Goal = Cash Available + Equity + Desired Loan Amount * * @param params.cashAvailable - Cash available * @param params.equity - Equity * @param params.desiredLoanAmount - Desired loan amount * @returns Buying goal */ function calculateBuyingGoal(params) { var cashAvailable = params.cashAvailable, equity = params.equity, desiredLoanAmount = params.desiredLoanAmount; return Math.max(cashAvailable, 0) + Math.max(equity, 0) + Math.max(desiredLoanAmount, 0); } /** * Calculate buying power * Buying Power = Cash Available + Equity + Max Loan Amount. * * @param params.cashAvailable - Cash available * @param params.equity - Equity * @param params.maxLoanAmount - Max loan amount * @returns Buying power */ function calculateBuyingPower(params) { var cashAvailable = params.cashAvailable, equity = params.equity, maxLoanAmount = params.maxLoanAmount; return Math.max(cashAvailable, 0) + Math.max(equity, 0) + Math.max(maxLoanAmount, 0); } exports.AUSTRALIAN_REVERSE_TAX_BRACKETS = AUSTRALIAN_REVERSE_TAX_BRACKETS; exports.AUSTRALIAN_REVERSE_TAX_BRACKETS_WITHOUT_MEDICARE_LEVY = AUSTRALIAN_REVERSE_TAX_BRACKETS_WITHOUT_MEDICARE_LEVY; exports.AUSTRALIAN_TAX_BRACKETS = AUSTRALIAN_TAX_BRACKETS; exports.DEFAULT_ASSESSMENT_BUFFER_RATE = DEFAULT_ASSESSMENT_BUFFER_RATE; exports.DEFAULT_HEM_BUFFER_RATE = DEFAULT_HEM_BUFFER_RATE; exports.DEFAULT_INTEREST_RATE = DEFAULT_INTEREST_RATE; exports.DEFAULT_LOAN_TERM = DEFAULT_LOAN_TERM; exports.DEFAULT_SHADED_RENTAL_INCOME_RATIO = DEFAULT_SHADED_RENTAL_INCOME_RATIO; exports.DEFAULT_SURPLUS_AVAILABLE_FOR_LOAN_RATE = DEFAULT_SURPLUS_AVAILABLE_FOR_LOAN_RATE; exports.HEM_DATA = HEM_DATA; exports.HEM_INCOME_BRACKETS = HEM_INCOME_BRACKETS; exports.MEDICARE_LEVY_RATE = MEDICARE_LEVY_RATE; exports.accountTypes = accountTypes; exports.calculateBorrowCapacity = calculateBorrowCapacity; exports.calculateBuyingGoal = calculateBuyingGoal; exports.calculateBuyingPower = calculateBuyingPower; exports.calculateCashAvailable = calculateCashAvailable; exports.calculateEquity = calculateEquity; exports.calculateEquityData = calculateEquityData; exports.calculateExcessMonthlySurplus = calculateExcessMonthlySurplus; exports.calculateHEMExpense = calculateHEMExpense; exports.calculateLVR = calculateLVR; exports.calculateLiability = calculateLiability; exports.calculateLoanRepayment = calculateLoanRepayment; exports.calculateMortgageLoan = calculateMortgageLoan; exports.calculatePropertiesEstimatedValue = calculatePropertiesEstimatedValue; exports.getLenderInfo = getLenderInfo; exports.getLinkedPropertyAccountIds = getLinkedPropertyAccountIds; exports.grossToNet = grossToNet; exports.liabilityAccountTypes = liabilityAccountTypes; exports.loanAccountTypes = loanAccountTypes; exports.netToGross = netToGross; exports.nonLiabilityAccountTypes = nonLiabilityAccountTypes; exports.nonSuperannuationAccountTypes = nonSuperannuationAccountTypes; exports.processScenarioHistoryData = processScenarioHistoryData; //# sourceMappingURL=borrow-capacity-lib.cjs.development.js.map