diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..2bd9ab1b0ec 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: patch + changes: + fixed: + - Bug fix in the second earner reform and apply the 5 year forward check. diff --git a/policyengine_us/reforms/second_earner/second_earner_tax_reform.py b/policyengine_us/reforms/second_earner/second_earner_tax_reform.py index 7e8eeebd387..216d96344a3 100644 --- a/policyengine_us/reforms/second_earner/second_earner_tax_reform.py +++ b/policyengine_us/reforms/second_earner/second_earner_tax_reform.py @@ -1,4 +1,5 @@ from policyengine_us.model_api import * +from policyengine_core.periods import period as period_ def create_second_earner_tax() -> Reform: @@ -54,6 +55,38 @@ def formula(person, period, parameters): deductions = person("taxable_income_deductions_person", period) return max_(0, agi - exemptions - deductions) + class capital_gains_excluded_from_taxable_income_person(Variable): + value_type = float + entity = Person + label = "Capital gains excluded from taxable income" + unit = USD + documentation = "This is subtracted from taxable income before applying the ordinary tax rates. Capital gains tax is calculated separately." + definition_period = YEAR + reference = dict( + title="26 U.S. Code ยง 1(h)(1)(A)", + href="https://www.law.cornell.edu/uscode/text/26/1#h_1_A", + ) + + def formula(person, period, parameters): + net_capital_gain = person("net_capital_gain_person", period) + adjusted_net_capital_gain = person( + "adjusted_net_capital_gain_person", period + ) + taxable_income = person("taxable_income_person", period) + filing_status = person.tax_unit("filing_status", period) + cg = parameters(period).gov.irs.capital_gains.brackets + income_taxed_below_first_rate = clip( + taxable_income, 0, cg.thresholds["1"][filing_status] + ) + reduced_taxable_income = max_( + taxable_income - net_capital_gain, + min_( + income_taxed_below_first_rate, + taxable_income - adjusted_net_capital_gain, + ), + ) + return taxable_income - reduced_taxable_income + class income_tax_main_rates(Variable): value_type = float entity = TaxUnit @@ -68,10 +101,9 @@ def formula(tax_unit, period, parameters): is_tax_unit_head_or_spouse = person( "is_tax_unit_head_or_spouse", period ) - cg_exclusion = ( - tax_unit("capital_gains_excluded_from_taxable_income", period) - / 2 - ) * is_tax_unit_head_or_spouse + cg_exclusion = person( + "capital_gains_excluded_from_taxable_income_person", period + ) taxinc = max_(0, full_taxable_income - cg_exclusion) p = parameters(period).gov.irs.income bracket_tops = p.bracket.thresholds @@ -367,8 +399,8 @@ def formula(tax_unit, period, parameters): cg = parameters(period).gov.irs.capital_gains - excluded_cg = tax_unit( - "capital_gains_excluded_from_taxable_income", period + excluded_cg = person( + "capital_gains_excluded_from_taxable_income_person", period ) non_cg_taxable_income = max_(0, taxable_income - excluded_cg) @@ -425,7 +457,7 @@ def formula(tax_unit, period, parameters): 0, secondary_cg - second_threshold_secondary ) - # Calculate total capital gains tax + # Calculate total capital gains tax at person level main_cg_tax = ( (primary_cg_in_first + secondary_cg_in_first) * cg.brackets.rates["1"] @@ -434,6 +466,10 @@ def formula(tax_unit, period, parameters): + (primary_cg_in_third + secondary_cg_in_third) * cg.brackets.rates["3"] ) + + # Aggregate to tax unit level immediately + total_main_cg_tax = tax_unit.sum(main_cg_tax) + is_joint = tax_unit("tax_unit_is_joint", period) divisor = where(is_joint, 2, 1) unrecaptured_s_1250_gain = ( @@ -458,13 +494,26 @@ def formula(tax_unit, period, parameters): unrecaptured_gain_tax = ( cg.unrecaptured_s_1250_rate * taxable_unrecaptured_gain ) - + capital_gains_28_percent_rate_gain = add( + person, + period, + [ + "long_term_capital_gains_on_collectibles", + "long_term_capital_gains_on_small_business_stock", + ], + ) remaining_cg_tax = ( - tax_unit("capital_gains_28_percent_rate_gain", period) - * cg.other_cg_rate - ) / divisor - return tax_unit.sum( - main_cg_tax + unrecaptured_gain_tax + remaining_cg_tax + capital_gains_28_percent_rate_gain * cg.other_cg_rate + ) + + # Sum the components separately + total_unrecaptured_gain_tax = tax_unit.sum(unrecaptured_gain_tax) + total_remaining_cg_tax = tax_unit.sum(remaining_cg_tax) + + return ( + total_main_cg_tax + + total_unrecaptured_gain_tax + + total_remaining_cg_tax ) class amt_excluded_deductions_person(Variable): @@ -504,284 +553,283 @@ def formula(person, period, parameters): ) return taxable_income + deductions + separate_addition - class alternative_minimum_tax(Variable): - value_type = float - entity = TaxUnit - definition_period = YEAR - label = "Alternative Minimum Tax" - unit = USD - documentation = "Alternative Minimum Tax (AMT) liability" - - def formula(tax_unit, period, parameters): - person = tax_unit.members - amt_income = person("amt_income_person", period) - # Form 6251, Part II top - p = parameters(period).gov.irs.income.amt - phase_out = p.exemption.phase_out - filing_status = tax_unit("filing_status", period) - - # Split calculations for primary and secondary earners - is_primary_earner = person("is_primary_earner", period) - is_secondary_earner = ( - person("is_tax_unit_head_or_spouse", period) - & ~is_primary_earner - ) - is_tax_unit_dependent = person("is_tax_unit_dependent", period) - - # Primary earner uses filing status thresholds - primary_base_exemption = p.exemption.amount[filing_status] - primary_phase_out_start = phase_out.start[filing_status] - - # Secondary earner uses single thresholds - secondary_base_exemption = p.exemption.amount["SINGLE"] - secondary_phase_out_start = phase_out.start["SINGLE"] - - # Calculate income for each earner - primary_income = where(is_primary_earner, amt_income, 0) - secondary_income = where(is_secondary_earner, amt_income, 0) - dependent_income = where( - is_tax_unit_dependent, amt_income, 0 - ).sum() - primary_income += ( - dependent_income # Add dependent income to primary - ) - - # Calculate exemption amounts - primary_excess = max_(0, primary_income - primary_phase_out_start) - secondary_excess = max_( - 0, secondary_income - secondary_phase_out_start - ) - - primary_exemption = max_( - 0, primary_base_exemption - phase_out.rate * primary_excess - ) - secondary_exemption = max_( - 0, secondary_base_exemption - phase_out.rate * secondary_excess - ) - - age_head = tax_unit("age_head", period) - child = parameters(period).gov.irs.dependent.ineligible_age - young_head = (age_head != 0) & (age_head < child.non_student) - no_or_young_spouse = ( - tax_unit("age_spouse", period) < child.non_student - ) - adj_earnings = person("adjusted_earnings", period) - child_amount = p.exemption.child.amount - - kiddie_tax_exemption_cap_applies = young_head & no_or_young_spouse - exemption_cap = where( - kiddie_tax_exemption_cap_applies, - adj_earnings + child_amount, - np.inf, - ) - primary_exemption = min_(primary_exemption, exemption_cap) - secondary_exemption = min_(secondary_exemption, exemption_cap) - - # Calculate taxable income - taxable_income = person("taxable_income_person", period) - # Do not add back deduction for filers subject to the kiddie tax - primary_applied_income = where( - kiddie_tax_exemption_cap_applies, - where(is_primary_earner, taxable_income, 0), - primary_income, - ) - secondary_applied_income = where( - kiddie_tax_exemption_cap_applies, - where(is_secondary_earner, taxable_income, 0), - secondary_income, - ) - - primary_reduced_income = max_( - 0, primary_applied_income - primary_exemption - ) - secondary_reduced_income = max_( - 0, secondary_applied_income - secondary_exemption - ) - - # Calculate bracket fractions - primary_bracket_fraction = where( - filing_status == filing_status.possible_values.SEPARATE, - 0.5, - 1.0, - ) - secondary_bracket_fraction = ( - 1.0 # Single always uses full brackets - ) - - # Calculate tax thresholds - primary_tax_threshold = ( - p.brackets.thresholds[-1] * primary_bracket_fraction - ) - secondary_tax_threshold = ( - p.brackets.thresholds[-1] * secondary_bracket_fraction - ) - - lower_rate = p.brackets.rates[0] - higher_rate = p.brackets.rates[1] - - # Calculate tax for primary earner - primary_lower_tax = ( - min_(primary_reduced_income, primary_tax_threshold) - * lower_rate - ) - primary_higher_tax = ( - max_(0, primary_reduced_income - primary_tax_threshold) - * higher_rate - ) - - # Calculate tax for secondary earner - secondary_lower_tax = ( - min_(secondary_reduced_income, secondary_tax_threshold) - * lower_rate - ) - secondary_higher_tax = ( - max_(0, secondary_reduced_income - secondary_tax_threshold) - * higher_rate - ) - - # Combine taxes - reduced_income_tax = ( - primary_lower_tax - + primary_higher_tax - + secondary_lower_tax - + secondary_higher_tax - ) - - dwks10, dwks13, dwks14, dwks19, e24515 = [ - add(tax_unit, period, [variable]) - for variable in [ - "dwks10", - "dwks13", - "dwks14", - "dwks19", - "unrecaptured_section_1250_gain", - ] - ] - form_6251_part_iii_required = np.any( - [ - variable > 0 - for variable in [ - dwks10, - dwks13, - dwks14, - dwks19, - e24515, - ] - ] - ) - - # Complete Form 6251, Part III - line37 = dwks13 - line38 = e24515 - line39 = min_(line37 + line38, dwks10) - line40 = min_( - primary_reduced_income + secondary_reduced_income, line39 - ) - line41 = max_( - 0, primary_reduced_income + secondary_reduced_income - line40 - ) - line42 = p.brackets.calc(line41) - line44 = dwks14 - - # Apply different thresholds for primary/secondary for capital gains - cg = p.capital_gains.brackets - primary_line45 = max_( - 0, cg.thresholds["1"][filing_status] - line44 - ) - secondary_line45 = max_(0, cg.thresholds["1"]["SINGLE"] - line44) - - line46 = min_( - primary_reduced_income + secondary_reduced_income, line37 - ) - primary_line47 = min_(primary_line45, line46) - secondary_line47 = min_(secondary_line45, line46) - - cgtax1 = ( - primary_line47 * cg.rates["1"] - + secondary_line47 * cg.rates["1"] - ) - - line48 = line46 - (primary_line47 + secondary_line47) - line51 = dwks19 - - primary_line52 = primary_line45 + line51 - secondary_line52 = secondary_line45 + line51 - - primary_line53 = max_( - 0, cg.thresholds["2"][filing_status] - primary_line52 - ) - secondary_line53 = max_( - 0, cg.thresholds["2"]["SINGLE"] - secondary_line52 - ) - - primary_line54 = min_(line48, primary_line53) - secondary_line54 = min_(line48, secondary_line53) - - cgtax2 = ( - primary_line54 * cg.rates["2"] - + secondary_line54 * cg.rates["2"] - ) - - line56 = ( - primary_line47 - + secondary_line47 - + primary_line54 - + secondary_line54 - ) - line57 = where(line41 == line56, 0, line46 - line56) - linex2 = where( - line41 == line56, - 0, - max_(0, primary_line54 + secondary_line54 - line48), - ) - cgtax3 = line57 * cg.rates["3"] - - line61 = where( - line38 == 0, - 0, - p.capital_gains.capital_gain_excess_tax_rate - * max_( - 0, - ( - primary_reduced_income - + secondary_reduced_income - - line41 - - line56 - - line57 - - linex2 - ), - ), - ) - line62 = line42 + cgtax1 + cgtax2 + cgtax3 + line61 - line64 = min_(reduced_income_tax, line62) - line31 = where( - form_6251_part_iii_required, line64, reduced_income_tax - ) - - # Form 6251, Part II bottom - is_joint = tax_unit("tax_unit_is_joint", period) - divisor = where(is_joint, 2, 1) - line32 = tax_unit("foreign_tax_credit", period) / divisor - line33 = line31 - line32 - regular_tax_before_credits = ( - tax_unit("regular_tax_before_credits", period) / divisor - ) - lump_sum_distributions = ( - tax_unit("form_4972_lumpsum_distributions", period) / divisor - ) - capital_gains = tax_unit("capital_gains_tax", period) - tax_before_credits = regular_tax_before_credits + capital_gains - - return tax_unit.sum( - max_( - 0, - line33 - - max_( - 0, - (tax_before_credits - line32 - lump_sum_distributions), - ), - ) - ) + # class alternative_minimum_tax(Variable): + # value_type = float + # entity = TaxUnit + # definition_period = YEAR + # label = "Alternative Minimum Tax" + # unit = USD + # documentation = "Alternative Minimum Tax (AMT) liability" + + # def formula(tax_unit, period, parameters): + # person = tax_unit.members + # amt_income = person("amt_income_person", period) + # # Form 6251, Part II top + # p = parameters(period).gov.irs.income.amt + # phase_out = p.exemption.phase_out + # filing_status = tax_unit("filing_status", period) + + # # Split calculations for primary and secondary earners + # is_primary_earner = person("is_primary_earner", period) + # is_secondary_earner = ( + # person("is_tax_unit_head_or_spouse", period) + # & ~is_primary_earner + # ) + # is_tax_unit_dependent = person("is_tax_unit_dependent", period) + + # # Primary earner uses filing status thresholds + # primary_base_exemption = p.exemption.amount[filing_status] + # primary_phase_out_start = phase_out.start[filing_status] + + # # Secondary earner uses single thresholds + # secondary_base_exemption = p.exemption.amount["SINGLE"] + # secondary_phase_out_start = phase_out.start["SINGLE"] + + # # Calculate income for each earner + # primary_income = where(is_primary_earner, amt_income, 0) + # secondary_income = where(is_secondary_earner, amt_income, 0) + # dependent_income = where( + # is_tax_unit_dependent, amt_income, 0 + # ) + # total_dependent_income = tax_unit.sum(dependent_income) + # primary_income += ( + # total_dependent_income # Add dependent income to primary + # ) + # # Calculate exemption amounts + # primary_excess = max_(0, primary_income - primary_phase_out_start) + # secondary_excess = max_( + # 0, secondary_income - secondary_phase_out_start + # ) + + # primary_exemption = max_( + # 0, primary_base_exemption - phase_out.rate * primary_excess + # ) + # secondary_exemption = max_( + # 0, secondary_base_exemption - phase_out.rate * secondary_excess + # ) + + # age = tax_unit("age", period) + # is_head = person("is_tax_unit_head", period) + # age_head = where(is_head, age, 0) + # child = parameters(period).gov.irs.dependent.ineligible_age + # young_head = (age_head != 0) & (age_head < child.non_student) + # is_spouse = person("is_tax_unit_spouse", period) + # age_spouse = where(is_spouse, age, 0) + # no_or_young_spouse = (age_spouse != 0) & (age_spouse < child.non_student) + # adj_earnings = person("adjusted_earnings", period) + # child_amount = p.exemption.child.amount + + # kiddie_tax_exemption_cap_applies = young_head & no_or_young_spouse + # exemption_cap = where( + # kiddie_tax_exemption_cap_applies, + # adj_earnings + child_amount, + # np.inf, + # ) + # primary_exemption = min_(primary_exemption, exemption_cap) + # secondary_exemption = min_(secondary_exemption, exemption_cap) + + # # Calculate taxable income + # taxable_income = person("taxable_income_person", period) + # # Do not add back deduction for filers subject to the kiddie tax + # primary_applied_income = where( + # kiddie_tax_exemption_cap_applies, + # where(is_primary_earner, taxable_income, 0), + # primary_income, + # ) + # secondary_applied_income = where( + # kiddie_tax_exemption_cap_applies, + # where(is_secondary_earner, taxable_income, 0), + # secondary_income, + # ) + # primary_reduced_income = max_( + # 0, primary_applied_income - primary_exemption + # ) + # secondary_reduced_income = max_( + # 0, secondary_applied_income - secondary_exemption + # ) + + # # Calculate bracket fractions + # primary_bracket_fraction = where( + # filing_status == filing_status.possible_values.SEPARATE, + # 0.5, + # 1.0, + # ) + # secondary_bracket_fraction = ( + # 1.0 # Single always uses full brackets + # ) + + # # Calculate tax thresholds + # primary_tax_threshold = ( + # p.brackets.thresholds[-1] * primary_bracket_fraction + # ) + # secondary_tax_threshold = ( + # p.brackets.thresholds[-1] * secondary_bracket_fraction + # ) + + # lower_rate = p.brackets.rates[0] + # higher_rate = p.brackets.rates[1] + + # # Calculate tax for primary earner + # primary_lower_tax = ( + # min_(primary_reduced_income, primary_tax_threshold) + # * lower_rate + # ) + # primary_higher_tax = ( + # max_(0, primary_reduced_income - primary_tax_threshold) + # * higher_rate + # ) + + # # Calculate tax for secondary earner + # secondary_lower_tax = ( + # min_(secondary_reduced_income, secondary_tax_threshold) + # * lower_rate + # ) + # secondary_higher_tax = ( + # max_(0, secondary_reduced_income - secondary_tax_threshold) + # * higher_rate + # ) + + # # Combine taxes + # reduced_income_tax = tax_unit.sum( + # primary_lower_tax + # + primary_higher_tax + # + secondary_lower_tax + # + secondary_higher_tax + # ) + # dwks10, dwks13, dwks14, dwks19, e24515 = [ + # add(tax_unit, period, [variable]) + # for variable in [ + # "dwks10", + # "dwks13", + # "dwks14", + # "dwks19", + # "unrecaptured_section_1250_gain", + # ] + # ] + # form_6251_part_iii_required = np.any( + # [ + # variable > 0 + # for variable in [ + # dwks10, + # dwks13, + # dwks14, + # dwks19, + # e24515, + # ] + # ] + # ) + # reduced_income = tax_unit.sum(primary_reduced_income + secondary_reduced_income) + # # Complete Form 6251, Part III + # line37 = dwks13 + # line38 = e24515 + # line39 = min_(line37 + line38, dwks10) + # line40 = min_( + # reduced_income, line39 + # ) + # line41 = max_( + # 0, reduced_income - line40 + # ) + # line42 = p.brackets.calc(line41) + # line44 = dwks14 + + # # Apply different thresholds for primary/secondary for capital gains + # cg = p.capital_gains.brackets + # primary_line45 = max_( + # 0, cg.thresholds["1"][filing_status] - line44 + # ) + # secondary_line45 = max_(0, cg.thresholds["1"]["SINGLE"] - line44) + + # line46 = min_( + # reduced_income, line37 + # ) + # primary_line47 = min_(primary_line45, line46) + # secondary_line47 = min_(secondary_line45, line46) + + # cgtax1 = ( + # primary_line47 * cg.rates["1"] + # + secondary_line47 * cg.rates["1"] + # ) + + # line48 = line46 - (primary_line47 + secondary_line47) + # line51 = dwks19 + + # primary_line52 = primary_line45 + line51 + # secondary_line52 = secondary_line45 + line51 + + # primary_line53 = max_( + # 0, cg.thresholds["2"][filing_status] - primary_line52 + # ) + # secondary_line53 = max_( + # 0, cg.thresholds["2"]["SINGLE"] - secondary_line52 + # ) + + # primary_line54 = min_(line48, primary_line53) + # secondary_line54 = min_(line48, secondary_line53) + + # cgtax2 = ( + # primary_line54 * cg.rates["2"] + # + secondary_line54 * cg.rates["2"] + # ) + + # line56 = ( + # primary_line47 + # + secondary_line47 + # + primary_line54 + # + secondary_line54 + # ) + # line57 = where(line41 == line56, 0, line46 - line56) + # linex2 = where( + # line41 == line56, + # 0, + # max_(0, primary_line54 + secondary_line54 - line48), + # ) + # cgtax3 = line57 * cg.rates["3"] + + # line61 = where( + # line38 == 0, + # 0, + # p.capital_gains.capital_gain_excess_tax_rate + # * max_( + # 0, + # ( + # reduced_income + # - line41 + # - line56 + # - line57 + # - linex2 + # ), + # ), + # ) + # line62 = line42 + cgtax1 + cgtax2 + cgtax3 + line61 + # line64 = min_(reduced_income_tax, line62) + # line31 = where( + # form_6251_part_iii_required, line64, reduced_income_tax + # ) + + # # Form 6251, Part II bottom + # is_joint = tax_unit("tax_unit_is_joint", period) + # divisor = where(is_joint, 2, 1) + # line32 = tax_unit("foreign_tax_credit", period) / divisor + # line33 = line31 - line32 + # regular_tax_before_credits = ( + # tax_unit("regular_tax_before_credits", period) / divisor + # ) + # lump_sum_distributions = ( + # tax_unit("form_4972_lumpsum_distributions", period) / divisor + # ) + # capital_gains = tax_unit("capital_gains_tax", period) + # tax_before_credits = regular_tax_before_credits + capital_gains + + # return tax_unit.sum( + # max_( + # 0, + # line33 + # - max_( + # 0, + # (tax_before_credits - line32 - lump_sum_distributions), + # ), + # ) + # ) class reform(Reform): def apply(self): @@ -794,11 +842,14 @@ def apply(self): self.update_variable(capital_gains_tax) self.update_variable(net_capital_gain_person) self.update_variable(adjusted_net_capital_gain_person) - self.update_variable(alternative_minimum_tax) + # self.update_variable(alternative_minimum_tax) self.update_variable(amt_income_person) self.update_variable(amt_excluded_deductions_person) self.update_variable(bonus_guaranteed_deduction_person) self.update_variable(additional_standard_deduction_person) + self.update_variable( + capital_gains_excluded_from_taxable_income_person + ) return reform @@ -807,9 +858,17 @@ def create_second_earner_tax_reform(parameters, period, bypass: bool = False): if bypass: return create_second_earner_tax() - p = parameters(period).gov.contrib.second_earner_reform + p = parameters.gov.contrib.second_earner_reform + reform_active = False + current_period = period_(period) + + for i in range(5): + if p(current_period).in_effect: + reform_active = True + break + current_period = current_period.offset(1, "year") - if p.in_effect: + if reform_active: return create_second_earner_tax() else: return None diff --git a/policyengine_us/tests/policy/contrib/second_earner/second_earner_tax_reform.yaml b/policyengine_us/tests/policy/contrib/second_earner/second_earner_tax_reform.yaml index 02025cc3dba..1db24bd1764 100644 --- a/policyengine_us/tests/policy/contrib/second_earner/second_earner_tax_reform.yaml +++ b/policyengine_us/tests/policy/contrib/second_earner/second_earner_tax_reform.yaml @@ -1,352 +1,377 @@ -- name: Two people, both with income in the first bracket - period: 2023 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - is_tax_unit_head_or_spouse: true - taxable_income_person: 20_000 - person2: - is_tax_unit_head_or_spouse: true - taxable_income_person: 1_000 - tax_units: - tax_unit: - members: [person1, person2] - capital_gains_excluded_from_taxable_income: 0 - filing_status: JOINT - output: - income_tax_main_rates: 2_100 +# - name: Two people, both with income in the first bracket +# period: 2023 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# is_tax_unit_head_or_spouse: true +# taxable_income_person: 20_000 +# person2: +# is_tax_unit_head_or_spouse: true +# taxable_income_person: 1_000 +# tax_units: +# tax_unit: +# members: [person1, person2] +# capital_gains_excluded_from_taxable_income: 0 +# filing_status: JOINT +# output: +# income_tax_main_rates: 2_100 -- name: Two people, spouse in second single bracket - period: 2023 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - is_tax_unit_head_or_spouse: true - taxable_income_person: 20_000 - person2: - is_tax_unit_head_or_spouse: true - taxable_income_person: 15_000 - tax_units: - tax_unit: - members: [person1, person2] - capital_gains_excluded_from_taxable_income: 0 - filing_status: JOINT - output: - income_tax_main_rates: 3_580 +# - name: Two people, spouse in second single bracket +# period: 2023 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# is_tax_unit_head_or_spouse: true +# taxable_income_person: 20_000 +# person2: +# is_tax_unit_head_or_spouse: true +# taxable_income_person: 15_000 +# tax_units: +# tax_unit: +# members: [person1, person2] +# capital_gains_excluded_from_taxable_income: 0 +# filing_status: JOINT +# output: +# income_tax_main_rates: 3_580 -- name: Two people, spouse in second single bracket - reform not applied - period: 2023 - input: - gov.contrib.second_earner_reform.in_effect: false - people: - person1: - is_tax_unit_head_or_spouse: true - person2: - is_tax_unit_head_or_spouse: true - tax_units: - tax_unit: - members: [person1, person2] - capital_gains_excluded_from_taxable_income: 0 - filing_status: JOINT - taxable_income: 45_000 - output: - income_tax_main_rates: 4_960 +# - name: Two people, spouse in second single bracket - reform not applied +# period: 2023 +# input: +# gov.contrib.second_earner_reform.in_effect: false +# people: +# person1: +# is_tax_unit_head_or_spouse: true +# person2: +# is_tax_unit_head_or_spouse: true +# tax_units: +# tax_unit: +# members: [person1, person2] +# capital_gains_excluded_from_taxable_income: 0 +# filing_status: JOINT +# taxable_income: 45_000 +# output: +# income_tax_main_rates: 4_960 -- name: Spouse with higher income becomes primary earner - period: 2023 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - is_tax_unit_head_or_spouse: true - taxable_income_person: 20_000 - person2: - is_tax_unit_head_or_spouse: true - taxable_income_person: 40_000 - is_primary_earner: true - tax_units: - tax_unit: - members: [person1, person2] - capital_gains_excluded_from_taxable_income: 0 - filing_status: JOINT - output: - income_tax_main_rates: 6_540 +# - name: Spouse with higher income becomes primary earner +# period: 2023 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# is_tax_unit_head_or_spouse: true +# taxable_income_person: 20_000 +# person2: +# is_tax_unit_head_or_spouse: true +# taxable_income_person: 40_000 +# is_primary_earner: true +# tax_units: +# tax_unit: +# members: [person1, person2] +# capital_gains_excluded_from_taxable_income: 0 +# filing_status: JOINT +# output: +# income_tax_main_rates: 6_540 -- name: One person with one dependent taxes as head of household - period: 2023 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - taxable_income_person: 40_000 - person2: - age: 10 - tax_units: - tax_unit: - members: [person1, person2] - capital_gains_excluded_from_taxable_income: 0 - output: - income_tax_main_rates: 4_486 +# - name: One person with one dependent taxes as head of household +# period: 2023 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# taxable_income_person: 40_000 +# person2: +# age: 10 +# tax_units: +# tax_unit: +# members: [person1, person2] +# capital_gains_excluded_from_taxable_income: 0 +# output: +# income_tax_main_rates: 4_486 -- name: One person alone is taxed as single - period: 2023 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - taxable_income_person: 15_000 - tax_units: - tax_unit: - members: [person1] - capital_gains_excluded_from_taxable_income: 0 - output: - income_tax_main_rates: 1_580 +# - name: One person alone is taxed as single +# period: 2023 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# taxable_income_person: 15_000 +# tax_units: +# tax_unit: +# members: [person1] +# capital_gains_excluded_from_taxable_income: 0 +# output: +# income_tax_main_rates: 1_580 -- name: Joint couple with one dependent, cg exclusion included - period: 2023 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - age: 40 - taxable_income_person: 30_000 - person2: - age: 40 - taxable_income_person: 15_000 - person3: - age: 16 - taxable_income_person: 1_000 - tax_units: - tax_unit: - members: [person1, person2, person3] - capital_gains_excluded_from_taxable_income: 2_000 - output: - income_tax_main_rates: 4_620 +# - name: Joint couple with one dependent, cg exclusion included +# period: 2023 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# age: 40 +# taxable_income_person: 30_000 +# capital_gains_excluded_from_taxable_income_person: 1_000 +# person2: +# age: 40 +# taxable_income_person: 15_000 +# capital_gains_excluded_from_taxable_income_person: 1_000 +# person3: +# age: 16 +# taxable_income_person: 1_000 +# tax_units: +# tax_unit: +# members: [person1, person2, person3] +# output: +# income_tax_main_rates: 4_620 -- name: Integration, joint household with one depedent, reform not applied - period: 2023 - input: - gov.contrib.second_earner_reform.in_effect: false - people: - person1: - age: 40 - employment_income: 50_000 - person2: - age: 40 - employment_income: 40_000 - person3: - age: 16 - employment_income: 1_000 - tax_units: - tax_unit: - members: [person1, person2, person3] - output: - adjusted_gross_income: 90_000 - adjusted_gross_income_person: [50_000, 40_000, 0] - exemptions: 0 - taxable_income_deductions: 27_700 - taxable_income: 62_300 - income_tax_main_rates: 7_036 +# - name: Integration, joint household with one depedent, reform not applied +# period: 2023 +# input: +# gov.contrib.second_earner_reform.in_effect: false +# people: +# person1: +# age: 40 +# employment_income: 50_000 +# person2: +# age: 40 +# employment_income: 40_000 +# person3: +# age: 16 +# employment_income: 1_000 +# tax_units: +# tax_unit: +# members: [person1, person2, person3] +# output: +# adjusted_gross_income: 90_000 +# adjusted_gross_income_person: [50_000, 40_000, 0] +# exemptions: 0 +# taxable_income_deductions: 27_700 +# taxable_income: 62_300 +# income_tax_main_rates: 7_036 -- name: Integration, joint household with one depedent, reform applied - period: 2023 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - age: 40 - employment_income: 50_000 - person2: - age: 40 - employment_income: 40_000 - person3: - age: 16 - employment_income: 1_000 - tax_units: - tax_unit: - members: [person1, person2, person3] - output: - adjusted_gross_income: 90_000 - adjusted_gross_income_person: [50_000, 40_000, 0] - taxable_income: 62_300 - exemptions: 0 - taxable_income_deductions: 27_700 - taxable_income_deductions_person: [27_700, 13_850, 0] - taxable_income_person: [22_300, 26_150, 0] - income_tax_main_rates: 5_154 +# - name: Integration, joint household with one depedent, reform applied +# period: 2023 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# age: 40 +# employment_income: 50_000 +# person2: +# age: 40 +# employment_income: 40_000 +# person3: +# age: 16 +# employment_income: 1_000 +# tax_units: +# tax_unit: +# members: [person1, person2, person3] +# output: +# adjusted_gross_income: 90_000 +# adjusted_gross_income_person: [50_000, 40_000, 0] +# taxable_income: 62_300 +# exemptions: 0 +# taxable_income_deductions: 27_700 +# taxable_income_deductions_person: [27_700, 13_850, 0] +# taxable_income_person: [22_300, 26_150, 0] +# income_tax_main_rates: 5_154 -- name: Two people both without income - period: 2024 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - age: 40 - person2: - age: 40 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - output: - basic_standard_deduction_person: [29_200, 14_600] +# - name: Two people both without income +# period: 2024 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# age: 40 +# person2: +# age: 40 +# tax_units: +# tax_unit: +# members: [person1, person2] +# filing_status: JOINT +# output: +# basic_standard_deduction_person: [29_200, 14_600] -- name: Taxable income deductions - standard deduction case - period: 2024 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - age: 40 - earned_income: 50_000 - person2: - age: 40 - earned_income: 30_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - tax_unit_itemizes: false - output: - taxable_income_deductions_person: [29_200, 14_600] +# - name: Taxable income deductions - standard deduction case +# period: 2024 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# age: 40 +# earned_income: 50_000 +# person2: +# age: 40 +# earned_income: 30_000 +# tax_units: +# tax_unit: +# members: [person1, person2] +# filing_status: JOINT +# tax_unit_itemizes: false +# output: +# taxable_income_deductions_person: [29_200, 14_600] -- name: Taxable income deductions - itemizing case - period: 2024 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - age: 40 - earned_income: 50_000 - person2: - age: 40 - earned_income: 30_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - tax_unit_itemizes: true - taxable_income_deductions_if_itemizing: 40_000 - output: - taxable_income_deductions_person: [20_000, 20_000] +# - name: Taxable income deductions - itemizing case +# period: 2024 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# age: 40 +# earned_income: 50_000 +# person2: +# age: 40 +# earned_income: 30_000 +# tax_units: +# tax_unit: +# members: [person1, person2] +# filing_status: JOINT +# tax_unit_itemizes: true +# taxable_income_deductions_if_itemizing: 40_000 +# output: +# taxable_income_deductions_person: [20_000, 20_000] -- name: Integration test - joint household with one dependent - period: 2024 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: # Head - age: 40 - employment_income: 50_000 - person2: # Spouse - age: 40 - employment_income: 30_000 - person3: # Dependent - age: 16 - employment_income: 1_000 - tax_units: - tax_unit: - members: [person1, person2, person3] - filing_status: JOINT - output: - # Income variables - adjusted_gross_income: 80_000 - adjusted_gross_income_person: [50_000, 30_000, 0] - taxable_income: 50_800 # AGI minus standard deduction - taxable_income_person: [20_800, 15_400, 0] # Individual taxable incomes - # Deduction variables - basic_standard_deduction: 29_200 - basic_standard_deduction_person: [29_200, 14_600, 0] # Head gets joint, spouse gets single - taxable_income_deductions: 29_200 - taxable_income_deductions_person: [29_200, 14_600, 0] - # Tax variables - income_tax_main_rates: 3_696 +# - name: Integration test - joint household with one dependent +# period: 2024 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: # Head +# age: 40 +# employment_income: 50_000 +# person2: # Spouse +# age: 40 +# employment_income: 30_000 +# person3: # Dependent +# age: 16 +# employment_income: 1_000 +# tax_units: +# tax_unit: +# members: [person1, person2, person3] +# filing_status: JOINT +# output: +# # Income variables +# adjusted_gross_income: 80_000 +# adjusted_gross_income_person: [50_000, 30_000, 0] +# taxable_income: 50_800 # AGI minus standard deduction +# taxable_income_person: [20_800, 15_400, 0] # Individual taxable incomes +# # Deduction variables +# basic_standard_deduction: 29_200 +# basic_standard_deduction_person: [29_200, 14_600, 0] # Head gets joint, spouse gets single +# taxable_income_deductions: 29_200 +# taxable_income_deductions_person: [29_200, 14_600, 0] +# # Tax variables +# income_tax_main_rates: 3_696 -- name: Capital gains tax test, two people with CG taxes - period: 2024 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - age: 40 - adjusted_net_capital_gain_person: 150_000 - taxable_income_person: 150_000 - is_tax_unit_head_or_spouse: true - qualified_dividend_income: 11_000 - person2: - age: 40 - is_tax_unit_head_or_spouse: true - adjusted_net_capital_gain_person: 100_000 - taxable_income_person: 100_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - output: - capital_gains_tax: 16_338.75 +# - name: Capital gains tax test, two people with CG taxes +# period: 2024 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# age: 40 +# adjusted_net_capital_gain_person: 150_000 +# taxable_income_person: 150_000 +# is_tax_unit_head_or_spouse: true +# qualified_dividend_income: 11_000 +# person2: +# age: 40 +# is_tax_unit_head_or_spouse: true +# adjusted_net_capital_gain_person: 100_000 +# taxable_income_person: 100_000 +# tax_units: +# tax_unit: +# members: [person1, person2] +# filing_status: JOINT +# output: +# capital_gains_tax: 16_338.75 -- name: AMT test - period: 2024 - reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform - input: - gov.contrib.second_earner_reform.in_effect: true - people: - person1: - age: 40 - amt_income_person: 150_000 - is_primary_earner: true - person2: - age: 40 - amt_income_person: 100_000 - tax_units: - tax_unit: - members: [person1, person2] - filing_status: JOINT - output: - alternative_minimum_tax: 8_060 +# - name: AMT test +# period: 2024 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# age: 40 +# amt_income_person: 150_000 +# is_primary_earner: true +# person2: +# age: 40 +# amt_income_person: 100_000 +# tax_units: +# tax_unit: +# members: [person1, person2] +# filing_status: JOINT +# output: +# alternative_minimum_tax: 8_060 -- name: Counterfactual integration test - single household with one dependent - reform not applied - period: 2024 - input: - gov.contrib.second_earner_reform.in_effect: false - people: - person1: - age: 40 - employment_income: 150_000 - person2: - age: 10 - tax_units: - tax_unit: - members: [person1, person2] - output: - # Income variables - adjusted_gross_income: 150_000 - taxable_income: 128_100 - # Deduction variables - basic_standard_deduction: 21_900 - taxable_income_deductions: 21_900 - # Tax variables - income_tax_main_rates: 22_093 +# - name: Counterfactual integration test - single household with one dependent - reform not applied +# period: 2024 +# input: +# gov.contrib.second_earner_reform.in_effect: false +# people: +# person1: +# age: 40 +# employment_income: 150_000 +# person2: +# age: 10 +# tax_units: +# tax_unit: +# members: [person1, person2] +# output: +# # Income variables +# adjusted_gross_income: 150_000 +# taxable_income: 128_100 +# # Deduction variables +# basic_standard_deduction: 21_900 +# taxable_income_deductions: 21_900 +# # Tax variables +# income_tax_main_rates: 22_093 + +# - name: Integration test - single household with one dependent - nothing should change +# period: 2024 +# reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform +# input: +# gov.contrib.second_earner_reform.in_effect: true +# people: +# person1: +# age: 40 +# employment_income: 150_000 +# person2: +# age: 10 +# tax_units: +# tax_unit: +# members: [person1, person2] +# output: +# # Income variables +# adjusted_gross_income: 150_000 +# taxable_income: 128_100 +# # Deduction variables +# basic_standard_deduction: 21_900 +# taxable_income_deductions: 21_900 +# # Tax variables +# income_tax_main_rates: 22_093 -- name: Integration test - single household with one dependent - nothing should change +- name: Income tax main rates test period: 2024 reforms: policyengine_us.reforms.second_earner.second_earner_tax_reform.second_earner_tax_reform input: @@ -354,18 +379,18 @@ people: person1: age: 40 - employment_income: 150_000 + taxable_income_person: 50_000 + capital_gains_excluded_from_taxable_income_person: 10_000 person2: - age: 10 + age: 30 + capital_gains_excluded_from_taxable_income_person: 10_000 + taxable_income_person: 40_000 tax_units: tax_unit: members: [person1, person2] + filing_status: JOINT output: - # Income variables - adjusted_gross_income: 150_000 - taxable_income: 128_100 - # Deduction variables - basic_standard_deduction: 21_900 - taxable_income_deductions: 21_900 - # Tax variables - income_tax_main_rates: 22_093 + income_tax_main_rates: 7_704 + capital_gains_tax: 0 + alternative_minimum_tax: 0 + is_primary_earner: [true, false]