## SOLVED: How to compute end of year totals for 401k monthly contributions?

 Jan '23 I currently working within Playground and macOS Command Line Tool project and am able to successfully print month-by-month contributions to a 401k account, both personal contributions and company direct contributions (not matching) dividing each between roth and traditional in accordance with user preferences. I am also able to provide COMBINED running totals (i.e. March's personal combined roth and traditional contribution total is the sum of January, February and March contributions). The problem I am currently struggling with is computing December totals for INDIVIDUAL groups. I need to compute the Jan-through-Dec totals for each of the following SEPARATELY: personal roth, personal traditional, personal 401a, company roth, company traditional, monthly take home, and 401k excess. There is some extraneous content within the code below as a result of my unsuccessful attempts. ``````let pennyRoundingBehavior = NSDecimalNumberHandler( roundingMode: .bankers, scale: 2, raiseOnExactness: false, raiseOnOverflow: true, raiseOnUnderflow: true, raiseOnDivideByZero: true ) func roundToNearestPenny(percentage: Decimal, of dollarAmount: Decimal) -> Decimal { assert((0...100).contains(percentage)) let x = ((dollarAmount * percentage / 100) as NSDecimalNumber) return x.rounding(accordingToBehavior: pennyRoundingBehavior) as Decimal } enum ContributionMonth: Int, CaseIterable { case January, February, March, April, May, June, July, August, September, October, November, December static var count:Int { allCases.count } } struct MonthlyContributions { var roth : Decimal = 0 var traditional : Decimal = 0 var rothCompany : Decimal = 0 var traditionalCompany : Decimal = 0 var yearToDate : Decimal = 0 var yearToDate401a : Decimal = 0 var yearToDateRoth : Decimal = 0 var yearToDateAll : Decimal = 0 var pers401a : Decimal = 0 var excessCompany401k : Decimal = 0 var sumOfPersRoth : Decimal = 0 } struct ContributionSplitInfo { // These are static because they are shared among all employees. static let personal401kLimit : Decimal = 22500.0 static let combined401kAnnualLimit : Decimal = 66000.0 static let companyDirContPercentage : Decimal = 16.0 /* These are provided as convenience, and because if for some reason they need to be individualized for each employee, that can be done without affecting the code that uses it. For example, tax law could change limits, but existing employees might be grandfathered with the old limits. Similarly the company might change its matching rate for new hires, but not for new hires, or might have increasing matching with years of service. */ var personal401kAnnualLimit : Decimal { Self.personal401kLimit } var combined401kAnnualLimit : Decimal { Self.combined401kAnnualLimit } var companyDirContPercentage : Decimal { Self.companyDirContPercentage } // These are stored instance properties because each employee can set them // differently var personal401kPercentage : Decimal var rothPercentage : Decimal } extension MonthlyContributions { var total: Decimal { roth + traditional } var companyTotal: Decimal { rothCompany + traditionalCompany } var takeHome: Decimal { monthlyPay - (roth + traditional + pers401a)} var allPersContributions: Decimal { roth + traditional + pers401a} func nextMonth(pay: Decimal, using splitInfo: ContributionSplitInfo) -> Self { assert((0...100).contains(splitInfo.personal401kPercentage)) assert((0...100).contains(splitInfo.companyDirContPercentage)) assert((0...100).contains(splitInfo.rothPercentage)) assert(splitInfo.personal401kAnnualLimit >= 0) assert(splitInfo.combined401kAnnualLimit >= 0) assert(pay >= 0) let (personal401kContribution, prelimit401aContribution) = self.personal401kContribution(fromPay: pay, using: splitInfo) var combinedYearToDateContributions = yearToDateAll let personal401aContribution = clampContribution( prelimit401aContribution, whenCumulativeValue: combinedYearToDateContributions, exceeds: splitInfo.combined401kAnnualLimit ) combinedYearToDateContributions += personal401aContribution let company401kContribution = self.company401kContribution( fromPersonalContribution: personal401kContribution, andCombinedYearToDateContributions: &combinedYearToDateContributions, using: splitInfo ) let (personalTraditional, personalRoth) = split401kContribution( contribution: personal401kContribution, rothPercentage: splitInfo.rothPercentage ) let (companyTraditional, companyRoth) = split401kContribution( contribution: company401kContribution, rothPercentage: splitInfo.rothPercentage ) let excessCompany401k = excess401k(companyContribution: company401kContribution) let contributions = Self( roth : personalRoth, traditional : personalTraditional, rothCompany : companyRoth, traditionalCompany: companyTraditional, yearToDate : yearToDate + personal401kContribution, yearToDate401a : yearToDate + personal401kContribution, yearToDateRoth : yearToDate + personalRoth, yearToDateAll : combinedYearToDateContributions, pers401a : personal401aContribution, excessCompany401k : excessCompany401k, sumOfPersRoth : sumOfPersRoth + personalRoth //< added this ) assert(contributions.yearToDate <= splitInfo.personal401kAnnualLimit) assert(contributions.yearToDateAll <= splitInfo.combined401kAnnualLimit) return contributions } private func sumOfContribution( contribution1: Decimal, contributionType: Decimal) -> Decimal { let endOfYearTotal = contribution1 + yearToDate return endOfYearTotal } private func split401kContribution( contribution: Decimal, rothPercentage: Decimal) -> (traditional: Decimal, roth: Decimal) { let roth = roundToNearestPenny( percentage: rothPercentage, of: contribution ) return (contribution - roth, roth) } private func personal401kContribution( fromPay pay: Decimal, using splitInfo: ContributionSplitInfo) -> (contribution: Decimal, unused: Decimal) { let prelimitContribution = roundToNearestPenny( percentage: splitInfo.personal401kPercentage, of: pay ) let actualContribution = clampContribution( prelimitContribution, whenCumulativeValue: yearToDate, exceeds: splitInfo.personal401kAnnualLimit ) return (actualContribution, prelimitContribution - actualContribution) } private func company401kContribution( fromPersonalContribution personalContribution: Decimal, andCombinedYearToDateContributions yearToDate: inout Decimal, using splitInfo: ContributionSplitInfo) -> Decimal { yearToDate += personalContribution let companyCombinedContribution = roundToNearestPenny( percentage: splitInfo.companyDirContPercentage, of: monthlyPay ) let companyContribution = clampContribution( companyCombinedContribution, whenCumulativeValue: yearToDate, exceeds: splitInfo.combined401kAnnualLimit ) yearToDate += companyContribution return companyContribution } private func excess401k( companyContribution: Decimal) -> Decimal { let excess401k = companyContribution == 0 ? roundToNearestPenny(percentage: splitInfo.companyDirContPercentage, of: monthlyPay) : 0 return excess401k } } extension Array where Element == MonthlyContributions { subscript (index: ContributionMonth) -> Element { self[index.rawValue] } } func clampContribution( _ current : Decimal, whenCumulativeValue cummulative: Decimal, exceeds limit : Decimal) -> Decimal { min(max(0, limit - cummulative), current) } func computeMonthlyContributions( monthlyPay : Decimal, using splitInfo : ContributionSplitInfo) -> [MonthlyContributions] { var contribution = MonthlyContributions() var monthlyContributions: [MonthlyContributions] = [] monthlyContributions.reserveCapacity(ContributionMonth.count) for _ in ContributionMonth.allCases { contribution = contribution.nextMonth(pay: monthlyPay, using: splitInfo) monthlyContributions.append(contribution) } return monthlyContributions } var monthlyPay: Decimal = 12758.0 let splitInfo = ContributionSplitInfo(personal401kPercentage: 100.0, rothPercentage: 10.0) let monthlyContributions = computeMonthlyContributions(monthlyPay: monthlyPay, using: splitInfo) for month in ContributionMonth.allCases { let curContribution = monthlyContributions[month] print("Contribution for \(month)") print(" Traditional : \$\(curContribution.traditional)") print(" Roth : \$\(curContribution.roth)") print(" Pers 401a : \$\(curContribution.pers401a)") print(" Company Trad : \$\(curContribution.traditionalCompany)") print(" Company Roth : \$\(curContribution.rothCompany)") print(" Pers Year-To-Date : \$\(curContribution.yearToDate)") print(" Combined Year-To-Date: \$\(curContribution.yearToDateAll)") print(" Company 401k Excess : \$\(curContribution.excessCompany401k)") print(" Take Home Pay : \$\(curContribution.takeHome)") print(" Total Pers Roth : \$\(curContribution.sumOfPersRoth)") print() } `````` Here is what prints out: ``````Contribution for January Traditional : \$11482.2 Roth : \$1275.8 Pers 401a : \$0 Company Trad : \$1837.15 Company Roth : \$204.13 Pers Year-To-Date : \$12758 Combined Year-To-Date: \$14799.28 Company 401k Excess : \$0 Take Home Pay : \$0 Total Pers Roth : \$0 Contribution for February Traditional : \$8767.8 Roth : \$974.2 Pers 401a : \$3016 Company Trad : \$1837.15 Company Roth : \$204.13 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$29598.56 Company 401k Excess : \$0 Take Home Pay : \$0 Total Pers Roth : \$0 Contribution for March Traditional : \$0 Roth : \$0 Pers 401a : \$12758 Company Trad : \$1837.15 Company Roth : \$204.13 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$44397.84 Company 401k Excess : \$0 Take Home Pay : \$0 Total Pers Roth : \$0 Contribution for April Traditional : \$0 Roth : \$0 Pers 401a : \$12758 Company Trad : \$1837.15 Company Roth : \$204.13 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$59197.12 Company 401k Excess : \$0 Take Home Pay : \$0 Total Pers Roth : \$0 Contribution for May Traditional : \$0 Roth : \$0 Pers 401a : \$6802.88 Company Trad : \$0 Company Roth : \$0 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$66000 Company 401k Excess : \$2041.28 Take Home Pay : \$5955.12 Total Pers Roth : \$0 Contribution for June Traditional : \$0 Roth : \$0 Pers 401a : \$0 Company Trad : \$0 Company Roth : \$0 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$66000 Company 401k Excess : \$2041.28 Take Home Pay : \$12758 Total Pers Roth : \$0 Contribution for July Traditional : \$0 Roth : \$0 Pers 401a : \$0 Company Trad : \$0 Company Roth : \$0 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$66000 Company 401k Excess : \$2041.28 Take Home Pay : \$12758 Total Pers Roth : \$0 Contribution for August Traditional : \$0 Roth : \$0 Pers 401a : \$0 Company Trad : \$0 Company Roth : \$0 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$66000 Company 401k Excess : \$2041.28 Take Home Pay : \$12758 Total Pers Roth : \$0 Contribution for September Traditional : \$0 Roth : \$0 Pers 401a : \$0 Company Trad : \$0 Company Roth : \$0 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$66000 Company 401k Excess : \$2041.28 Take Home Pay : \$12758 Total Pers Roth : \$0 Contribution for October Traditional : \$0 Roth : \$0 Pers 401a : \$0 Company Trad : \$0 Company Roth : \$0 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$66000 Company 401k Excess : \$2041.28 Take Home Pay : \$12758 Total Pers Roth : \$0 Contribution for November Traditional : \$0 Roth : \$0 Pers 401a : \$0 Company Trad : \$0 Company Roth : \$0 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$66000 Company 401k Excess : \$2041.28 Take Home Pay : \$12758 Total Pers Roth : \$0 Contribution for December Traditional : \$0 Roth : \$0 Pers 401a : \$0 Company Trad : \$0 Company Roth : \$0 Pers Year-To-Date : \$22500 Combined Year-To-Date: \$66000 Company 401k Excess : \$2041.28 Take Home Pay : \$12758 Total Pers Roth : \$0 Program ended with exit code: 0`````` The print is accurate with exception of "Total Pers Roth." Given the above monthly pay, personal401kPercentage and rothPercentage, the total sum of personal roth should be \$2,250. The total sum of personal traditional contributions should be \$20,250. The total for company roth: \$816. Total for company traditional: \$7348. Total for 401a: \$35,335. Total for monthly take-home pay: \$95,256. Total 401k excess: \$16,330. Jan '23 In the supplied code you never actually assign a value to it (sumOfPersRoth). You have the variable declaration, and the print statement, but that is it. Do you set `sumOfPersRoth` to a value elsewhere in your code? Jan '23 I adjusted the following `````` let contributions = Self( roth : personalRoth, traditional : personalTraditional, rothCompany : companyRoth, traditionalCompany: companyTraditional, yearToDate : yearToDate + personal401kContribution, yearToDate401a : yearToDate + personal401kContribution, yearToDateRoth : yearToDate + personalRoth, yearToDateAll : combinedYearToDateContributions, pers401a : personal401aContribution, excessCompany401k : excessCompany401k, sumOfPersRoth : sumOfPersRoth + personalRoth //< added this )`````` Jan '23 This solved the issue: `````` struct MonthlyContributions { var roth : Decimal = 0 var traditional : Decimal = 0 var rothCompany : Decimal = 0 var traditionalCompany : Decimal = 0 var yearToDate : Decimal = 0 var yearToDate401a : Decimal = 0 var yearToDateRoth : Decimal = 0 var yearToDateAll : Decimal = 0 var pers401a : Decimal = 0 var excessCompany401k : Decimal = 0 var sumOfPersRoth : Decimal = 0 var sumOfPersTrad : Decimal = 0 var sumOfCompRoth : Decimal = 0 var sumOfCompTrad : Decimal = 0 var sumOf401a : Decimal = 0 var sumOf401kExcess : Decimal = 0 var sumOfTakeHome : Decimal = 0 } .... let contributions = Self( roth : personalRoth, traditional : personalTraditional, rothCompany : companyRoth, traditionalCompany: companyTraditional, yearToDate : yearToDate + personal401kContribution, yearToDate401a : yearToDate + personal401kContribution, yearToDateRoth : yearToDate + personalRoth, yearToDateAll : combinedYearToDateContributions, pers401a : personal401aContribution, excessCompany401k : excessCompany401k, sumOfPersRoth : sumOfPersRoth + personalRoth, sumOfPersTrad : sumOfPersTrad + personalTraditional, sumOfCompRoth: sumOfCompRoth + companyRoth, sumOfCompTrad: sumOfCompTrad + companyTraditional, sumOf401a: sumOf401a + personal401aContribution, sumOf401kExcess: sumOf401kExcess + excessCompany401k, sumOfTakeHome: sumOfTakeHome + takeHome )``````

SPONSORED AppSweep by Guardsquare helps developers automate the mobile app security testing process with fast, free scans. By using AppSweep’s actionable recommendations, developers can improve the security posture of their apps in accordance with security standards like OWASP.

Sponsor Hacking with Swift and reach the world's largest Swift community!