Skip to content

Commit

Permalink
Add tip and overtime income reform
Browse files Browse the repository at this point in the history
Fixes #45
  • Loading branch information
PavelMakarchuk committed Oct 25, 2024
1 parent 2949261 commit 9d0d24e
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 39 deletions.
7 changes: 5 additions & 2 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

with income_col:
st.markdown("### Income Information")
income, social_security_retirement = render_income_inputs()
# Update to unpack all four return values
income, social_security_retirement, tip_income, overtime_income = render_income_inputs()
itemized_deductions = render_itemized_deductions()

# Calculate button
Expand All @@ -35,6 +36,8 @@
"spouse_age": spouse_age,
"income": income,
"social_security_retirement": social_security_retirement,
"tip_income": tip_income,
"overtime_income": overtime_income,
**itemized_deductions
}

Expand All @@ -55,7 +58,7 @@

with tab2:
# Display credit components
credit_df = format_credit_components(results_df, state) # Pass the state code
credit_df = format_credit_components(results_df, state)
if credit_df is not None:
st.markdown(credit_df.to_markdown())
else:
Expand Down
5 changes: 5 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@
[Learn more about the Harris CTC](https://policyengine.org/us/research/harris-ctc)
- **High Earners Reform**: Adjustments to tax rates and thresholds for high-income earners
- **Restored EITC**: Bringing back the expanded American Rescue Plan version of the Earned Income Tax Credit
- **Tip Income Tax Relief**: Exempting tip income from both income and payroll taxes
### Trump Economic Package
The Trump platform includes:
- **Social Security Tax Exemption**: Eliminating taxes on Social Security benefits
[Learn more about the Social Security proposal](https://policyengine.org/us/research/social-security-tax-exemption)
- **Tip Income Tax Relief**: Exempting all tip income from both income and payroll taxes
- **Overtime Tax Relief**: Exempting all overtime income from both income and payroll taxes
"""

NOTES = """
Expand All @@ -32,4 +36,5 @@
- The calculator uses the PolicyEngine US microsimulation model.
- Actual impacts may vary based on individual circumstances and final policy implementations.
- Proposals are modeled based on currently available information and may be updated as more details are released.
- Tax exemptions for tip and overtime income apply to both income tax and payroll taxes under the specified reforms.
"""
28 changes: 27 additions & 1 deletion reforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@
},
"gov.irs.credits.eitc.phase_out.start[0].amount": {
"2025-01-01.2025-12-31": 13706,
},
# Tip Income Tax Exemption
"gov.contrib.tax_exempt.in_effect": {
"2025-01-01.2100-12-31": True
},
"gov.contrib.tax_exempt.tip_income.income_tax_exempt": {
"2025-01-01.2100-12-31": True
},
"gov.contrib.tax_exempt.tip_income.payroll_tax_exempt": {
"2025-01-01.2100-12-31": True
}
},
"Trump": {
Expand All @@ -78,6 +88,22 @@
},
"gov.irs.social_security.taxability.rate.base": {
"2024-01-01.2100-12-31": 0
},
# Trump Tax Exemptions
"gov.contrib.tax_exempt.in_effect": {
"2025-01-01.2100-12-31": True
},
"gov.contrib.tax_exempt.overtime.income_tax_exempt": {
"2025-01-01.2100-12-31": True
},
"gov.contrib.tax_exempt.overtime.payroll_tax_exempt": {
"2025-01-01.2100-12-31": True
},
"gov.contrib.tax_exempt.tip_income.income_tax_exempt": {
"2025-01-01.2100-12-31": True
},
"gov.contrib.tax_exempt.tip_income.payroll_tax_exempt": {
"2025-01-01.2100-12-31": True
}
}
}
}
116 changes: 84 additions & 32 deletions results.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,38 @@ def load_credits_from_yaml(package, resource_path):
def create_situation(state, is_married, child_ages, income, social_security_retirement,
head_age, spouse_age=None, medical_expenses=0, real_estate_taxes=0,
interest_expense=0, charitable_cash=0, charitable_non_cash=0,
qualified_business_income=0, casualty_loss=0):
qualified_business_income=0, casualty_loss=0, tip_income=0,
overtime_income=0, reform_name="Baseline"):
"""
Creates a situation dictionary for the simulation.
service_industry_wages are only included for specific reforms:
- For Harris reform: includes tip_income
- For Trump reform: includes both tip_income and overtime_income
- For Baseline: all income is treated as regular employment_income
"""
# Initialize person dict with common attributes
person_dict = {
"age": {YEAR: head_age},
"employment_income": {YEAR: income},
"social_security_retirement": {YEAR: social_security_retirement},
"medical_out_of_pocket_expenses": {YEAR: medical_expenses},
"interest_expense": {YEAR: interest_expense},
"charitable_cash_donations": {YEAR: charitable_cash},
"charitable_non_cash_donations": {YEAR: charitable_non_cash},
"qualified_business_income": {YEAR: qualified_business_income},
"casualty_loss": {YEAR: casualty_loss},
"real_estate_taxes": {YEAR: real_estate_taxes},
}

# Add service_industry_wages only for relevant reforms
if reform_name == "Harris":
person_dict["service_industry_wages"] = {YEAR: tip_income}
elif reform_name == "Trump":
person_dict["service_industry_wages"] = {YEAR: tip_income + overtime_income}

situation = {
"people": {
"adult": {
"age": {YEAR: head_age},
"employment_income": {YEAR: income},
"social_security_retirement": {YEAR: social_security_retirement},
"medical_out_of_pocket_expenses": {YEAR: medical_expenses},
"interest_expense": {YEAR: interest_expense},
"charitable_cash_donations": {YEAR: charitable_cash},
"charitable_non_cash_donations": {YEAR: charitable_non_cash},
"qualified_business_income": {YEAR: qualified_business_income},
"casualty_loss": {YEAR: casualty_loss},
"real_estate_taxes": {YEAR: real_estate_taxes},
},
"adult": person_dict
},
"families": {"family": {"members": ["adult"]}},
"marital_units": {"marital_unit": {"members": ["adult"]}},
Expand All @@ -53,16 +70,26 @@ def create_situation(state, is_married, child_ages, income, social_security_reti
"spm_units": {"household": {"members": ["adult"]}},
}

# Add children
for i, age in enumerate(child_ages):
child_id = f"child_{i}"
situation["people"][child_id] = {"age": {YEAR: age}}
for unit in ["families", "tax_units", "households", "spm_units"]:
situation[unit][list(situation[unit].keys())[0]]["members"].append(child_id)

# Add spouse if married
if is_married and spouse_age is not None:
situation["people"]["spouse"] = {
spouse_dict = {
"age": {YEAR: spouse_age},
"employment_income": {YEAR: 0},
}
# Add service_industry_wages for spouse only if reform applies
if reform_name == "Harris":
spouse_dict["service_industry_wages"] = {YEAR: 0}
elif reform_name == "Trump":
spouse_dict["service_industry_wages"] = {YEAR: 0}

situation["people"]["spouse"] = spouse_dict
for unit in ["families", "marital_units", "tax_units", "households", "spm_units"]:
situation[unit][list(situation[unit].keys())[0]]["members"].append("spouse")

Expand All @@ -78,19 +105,47 @@ def calculate_values(categories, simulation, year):
result_dict[category] = 0
return result_dict

def calculate_consolidated_results(reform_name, state, is_married, child_ages, income, social_security_retirement,
head_age, spouse_age=None, medical_expenses=0, real_estate_taxes=0,
interest_expense=0, charitable_cash=0, charitable_non_cash=0,
qualified_business_income=0, casualty_loss=0):
def calculate_consolidated_results(reform_name, state, is_married, child_ages, income,
social_security_retirement, head_age, spouse_age=None,
medical_expenses=0, real_estate_taxes=0, interest_expense=0,
charitable_cash=0, charitable_non_cash=0, qualified_business_income=0,
casualty_loss=0, tip_income=0, overtime_income=0):
"""
Calculates metrics for a single reform with detailed breakdowns.
"""
# Create situation dictionary
# For baseline, add all income to regular employment income
if reform_name == "Baseline":
total_income = income + tip_income + overtime_income
tip_income = 0
overtime_income = 0
else:
total_income = income
# For Harris reform, only tip income is exempt
if reform_name == "Harris":
total_income += overtime_income
overtime_income = 0
# For Trump reform, both tip and overtime remain separate
# (no need to modify as both will be handled in service_industry_wages)

# Create situation dictionary with reform name
situation = create_situation(
state, is_married, child_ages, income, social_security_retirement,
head_age, spouse_age, medical_expenses, real_estate_taxes, interest_expense,
charitable_cash, charitable_non_cash, qualified_business_income,
casualty_loss
state=state,
is_married=is_married,
child_ages=child_ages,
income=total_income,
social_security_retirement=social_security_retirement,
head_age=head_age,
spouse_age=spouse_age,
medical_expenses=medical_expenses,
real_estate_taxes=real_estate_taxes,
interest_expense=interest_expense,
charitable_cash=charitable_cash,
charitable_non_cash=charitable_non_cash,
qualified_business_income=qualified_business_income,
casualty_loss=casualty_loss,
tip_income=tip_income,
overtime_income=overtime_income,
reform_name=reform_name
)

# Set up simulation
Expand All @@ -101,14 +156,17 @@ def calculate_consolidated_results(reform_name, state, is_married, child_ages, i
reform = Reform.from_dict(reform_dict, country_id="us")
simulation = Simulation(reform=reform, situation=situation)

# # Get categories for credits
# federal_refundable_credits = IncomeTaxRefundableCredits.adds
# state_refundable_credits = StateRefundableCredits.adds
# Get metrics
household_net_income = simulation.calculate("household_net_income", YEAR)[0]
household_refundable_tax_credits = simulation.calculate("household_refundable_tax_credits", YEAR)[0]
household_tax_before_refundable_credits = simulation.calculate("household_tax_before_refundable_credits", YEAR)[0]

package = "policyengine_us"
resource_path_federal = "parameters/gov/irs/credits/refundable.yaml"
resource_path_state = f"parameters/gov/states/{state.lower()}/tax/income/credits/refundable.yaml"


# Get categories for credits
try:
federal_refundable_credits = load_credits_from_yaml(package, resource_path_federal)
except FileNotFoundError:
Expand All @@ -119,12 +177,6 @@ def calculate_consolidated_results(reform_name, state, is_married, child_ages, i
except FileNotFoundError:
state_refundable_credits = []


# Calculate main metrics
household_net_income = simulation.calculate("household_net_income", YEAR)[0]
household_refundable_tax_credits = simulation.calculate("household_refundable_tax_credits", YEAR)[0]
household_tax_before_refundable_credits = simulation.calculate("household_tax_before_refundable_credits", YEAR)[0]

# Calculate credit breakdowns
federal_credits_dict = calculate_values(federal_refundable_credits, simulation, YEAR)
state_credits_dict = calculate_values(state_refundable_credits, simulation, YEAR)
Expand Down
43 changes: 39 additions & 4 deletions ui_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,46 @@ def render_personal_info():
return is_married, state, child_ages, head_age, spouse_age

def render_income_inputs():
income = st.slider("Annual wages and salaries", min_value=0, max_value=500000, value=30000, step=500, format="$%d")
social_security_retirement = st.slider("Annual social security retirement income", min_value=0, max_value=500000, value=0, step=500, format="$%d")
income = st.slider(
"Annual wages and salaries",
min_value=0,
max_value=500000,
value=30000,
step=500,
format="$%d",
help="Regular wages and salaries (excluding overtime and tips)"
)

return income, social_security_retirement

social_security_retirement = st.slider(
"Annual social security retirement income",
min_value=0,
max_value=500000,
value=0,
step=500,
format="$%d"
)

tip_income = st.slider(
"Annual tip income",
min_value=0,
max_value=100000,
value=0,
step=100,
format="$%d",
help="Total annual income from tips"
)

overtime_income = st.slider(
"Annual overtime income",
min_value=0,
max_value=100000,
value=0,
step=100,
format="$%d",
help="Total annual income from overtime work"
)

return income, social_security_retirement, tip_income, overtime_income
def render_itemized_deductions():
show_itemized = st.expander("Itemized deduction sources", expanded=False)

Expand Down

0 comments on commit 9d0d24e

Please sign in to comment.