diff --git a/changelog.d/fix-schedule-c-category-losses-agi.fixed.md b/changelog.d/fix-schedule-c-category-losses-agi.fixed.md new file mode 100644 index 00000000000..9a4de707704 --- /dev/null +++ b/changelog.d/fix-schedule-c-category-losses-agi.fixed.md @@ -0,0 +1 @@ +Deduct a Schedule C loss in one SSTB category from AGI when the same person has Schedule C profit in the other category, and treat the two categories separately in the Wisconsin homestead loss add-back and the Kentucky and Maryland spouse loss allocations. diff --git a/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/disqualified_loss_sources.yaml b/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/disqualified_loss_sources.yaml index 7ab7c740d6b..a998975efd7 100644 --- a/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/disqualified_loss_sources.yaml +++ b/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/disqualified_loss_sources.yaml @@ -2,9 +2,11 @@ description: Wisconsin adds back the losses from these person-level income sourc values: 2021-01-01: # Schedule 4, line 2: net business loss from a sole proprietorship - # (Schedule C). SSTB and non-SSTB income are combined, as loss_ald - # deducts the loss of their total. - - total_self_employment_income + # (Schedule C). The line 11j instructions say to consider each business + # individually, so the SSTB and non-SSTB losses are added back + # separately, as loss_ald deducts them. + - self_employment_income + - sstb_self_employment_income - rental_income # line 4: net rental real estate and royalty loss # Form 4835 farm rental loss: rental real estate (71.52(1e)(d)) or net # farm loss (71.52(1e)(e)). diff --git a/policyengine_us/tests/core/test_dependent_losses_agi_invariants.py b/policyengine_us/tests/core/test_dependent_losses_agi_invariants.py index 432e36b895f..5ff3157bf0f 100644 --- a/policyengine_us/tests/core/test_dependent_losses_agi_invariants.py +++ b/policyengine_us/tests/core/test_dependent_losses_agi_invariants.py @@ -275,10 +275,9 @@ def test_units_without_dependents_match_all_member_formula(runs, units): """Differential check against the all-member formula.""" p = runs["irs"] filing_status = runs["with"]["filing_status"] - se = _by_person(units, "self_employment_income") + _by_person( - units, "sstb_self_employment_income" - ) - person_business = [se] + [_by_person(units, name) for name in BUSINESS_INPUTS[2:]] + # Each Schedule C category's profit and loss on its own, as + # irs_gross_income counts them (#9989). + person_business = [_by_person(units, name) for name in BUSINESS_INPUTS] income = sum(_unit_sum(runs, np.maximum(x, 0)) for x in person_business) loss = sum(_unit_sum(runs, np.maximum(-x, 0)) for x in person_business) limited_business_loss = np.minimum(loss, income + p.ald.loss.max[filing_status]) diff --git a/policyengine_us/tests/core/test_loss_ald_extraction_parity.py b/policyengine_us/tests/core/test_loss_ald_extraction_parity.py index d7c2ee4ccfb..5b6701637cb 100644 --- a/policyengine_us/tests/core/test_loss_ald_extraction_parity.py +++ b/policyengine_us/tests/core/test_loss_ald_extraction_parity.py @@ -216,8 +216,10 @@ def _pre_extraction_reference(unit, threshold, capital_cap): for person, dependent in unit["members"]: if dependent: continue + # Each Schedule C category is its own source, as in irs_gross_income. business_sources = ( - person["self_employment_income"] + person["sstb_self_employment_income"], + person["self_employment_income"], + person["sstb_self_employment_income"], person["farm_operations_income"], person["rental_income"], person["farm_rent_income"], diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/self_employment_income.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/self_employment_income.yaml index 526b7ca01e0..54b7d53a1c0 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/self_employment_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/self_employment_income.yaml @@ -54,3 +54,32 @@ sstb_self_employment_income_behavioral_response: 1_000 self_employment_income: 0 sstb_self_employment_income: -9_000 + +- name: Non-SSTB Schedule C loss offsets SSTB Schedule C profit in AGI + # Two Schedules C: line 31 of (5,000) and 5,000 both go to Schedule 1 + # line 3, which is 0, so Form 1040 line 11 AGI is 0. + period: 2024 + input: + self_employment_income: -5_000 + sstb_self_employment_income: 5_000 + output: + adjusted_gross_income: 0 + +- name: SSTB Schedule C loss offsets non-SSTB Schedule C profit in AGI + period: 2024 + input: + self_employment_income: 5_000 + sstb_self_employment_income: -5_000 + output: + adjusted_gross_income: 0 + +- name: Offsetting Schedule C results leave AGI equal to wages + # Form 1040 line 1z 50,000 plus Schedule 1 line 3 of 0 gives line 11 AGI + # of 50,000. + period: 2024 + input: + employment_income: 50_000 + self_employment_income: -5_000 + sstb_self_employment_income: 5_000 + output: + adjusted_gross_income: 50_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_federal_agi.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_federal_agi.yaml index 4d3e4e2e4a4..c6feabfc5df 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_federal_agi.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_federal_agi.yaml @@ -271,3 +271,30 @@ ky_agi: [50_000, -13_000] ky_modified_agi_if_joint: 37_000 ky_modified_agi_if_separate: 50_000 + +- name: A spouse's non-SSTB Schedule C loss offsets the same spouse's SSTB Schedule C profit + # 2025 Form 740 instructions, page 13, line 9: "Each spouse must claim his + # or her own income and deductions. The total of line 5, Columns A and B, + # must equal the combined federal adjusted gross income." The head's two + # Schedules C (line 31 of (5,000) and 5,000) net to 0 on Schedule 1 line 3, + # so the head's line 5 is 0 and the spouse's is the 40,000 of wages. + period: 2025 + input: + people: + head: + age: 45 + self_employment_income: -5_000 + sstb_self_employment_income: 5_000 + spouse: + age: 43 + employment_income: 40_000 + tax_units: + tax_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: KY + output: + adjusted_gross_income: 40_000 + ky_federal_agi: [0, 40_000] diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_disqualified_losses.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_disqualified_losses.yaml index 3b66f547433..e9dc4838dd7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_disqualified_losses.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_disqualified_losses.yaml @@ -163,3 +163,25 @@ state_code: WI output: wi_homestead_disqualified_losses: 0 + +- name: Case 9, a non-SSTB sole proprietorship loss is not netted against SSTB sole proprietorship income. + period: 2023 + absolute_error_margin: 0.01 + input: + people: + head: + age: 70 + self_employment_income: -5_000 + sstb_self_employment_income: 5_000 + tax_units: + tax_unit: + members: [head] + households: + household: + members: [head] + state_code: WI + output: + # 2023 Schedule H instructions, line 11j (page 16): consider each business + # individually and do not net a business with a gain against one with a + # loss (Example 1). Schedule 4, line 2 = 5,000. + wi_homestead_disqualified_losses: 5_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 2cea0b82040..744d7253c8a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -96,6 +96,6 @@ # Two Schedule C businesses (2023 instructions, page 16, Example 1): # Schedule 1 line 3 = 5,000 - 5,000 = 0, and Schedule 4 line 2 adds back # the 5,000 loss without netting. Household income = 0 + 5,000 = 5,000. - # loss_ald deducts the loss of total_self_employment_income (0 here), so - # the add-back follows that total and household income is the same. + # loss_ald deducts each category's loss on its own, so AGI is 0 and the + # add-back is the 5,000 non-SSTB loss. wi_homestead_income: 5_000 diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/limited_business_loss.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/limited_business_loss.py index 153c74d4982..cd1d758970d 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/limited_business_loss.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/limited_business_loss.py @@ -33,8 +33,12 @@ def formula(tax_unit, period, parameters): # business amounts flow into gross income and losses are deducted here, # so allowable business losses are capped at business income plus # the threshold. - indiv_se_income = max_(0, person("total_self_employment_income", period)) - indiv_se_loss = max_(0, -person("total_self_employment_income", period)) + # irs_gross_income counts each Schedule C category's profit on its + # own, so each category's loss is deducted on its own here. + non_sstb_se = person("self_employment_income", period) + sstb_se = person("sstb_self_employment_income", period) + indiv_se_income = max_(0, non_sstb_se) + max_(0, sstb_se) + indiv_se_loss = max_(0, -non_sstb_se) + max_(0, -sstb_se) self_employment_income = tax_unit.sum(not_dependent * indiv_se_income) self_employment_loss = tax_unit.sum(not_dependent * indiv_se_loss) diff --git a/policyengine_us/variables/gov/states/ky/tax/income/ky_federal_agi.py b/policyengine_us/variables/gov/states/ky/tax/income/ky_federal_agi.py index 5286ea249d3..f8d17c331c6 100644 --- a/policyengine_us/variables/gov/states/ky/tax/income/ky_federal_agi.py +++ b/policyengine_us/variables/gov/states/ky/tax/income/ky_federal_agi.py @@ -45,8 +45,10 @@ def formula(person, period, parameters): business_loss = max_(loss - capital_loss, 0) not_dependent = ~person("is_tax_unit_dependent", period) owned_business_loss = 0 + # Each Schedule C category's loss counts on its own, as in loss_ald. for source in ( - "total_self_employment_income", + "self_employment_income", + "sstb_self_employment_income", "farm_operations_income", "rental_income", "farm_rent_income", diff --git a/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_two_income_subtraction.py b/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_two_income_subtraction.py index 8db3537a2d3..69e54011116 100644 --- a/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_two_income_subtraction.py +++ b/policyengine_us/variables/gov/states/md/tax/income/agi/subtractions/md_two_income_subtraction.py @@ -1,9 +1,11 @@ from policyengine_us.model_api import * -# Person-level sources of the business losses in loss_ald. +# Person-level sources of the business losses in loss_ald, with each +# Schedule C category on its own, as loss_ald deducts them. BUSINESS_LOSS_SOURCES = [ - "total_self_employment_income", + "self_employment_income", + "sstb_self_employment_income", "farm_operations_income", "rental_income", "farm_rent_income",