taxforge 0.9.16__tar.gz → 0.9.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {taxforge-0.9.16 → taxforge-0.9.18}/PKG-INFO +1 -1
- {taxforge-0.9.16 → taxforge-0.9.18}/pyproject.toml +1 -1
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/aitax/aitax.py +7 -7
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/aitax/document_extractor.py +96 -1
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/aitax/state.py +77 -3
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/taxforge.egg-info/PKG-INFO +1 -1
- {taxforge-0.9.16 → taxforge-0.9.18}/README.md +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/setup.cfg +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/tests/test_fact_graph.py +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/tests/test_investments.py +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/tests/test_tax_engine.py +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/aitax/__init__.py +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/aitax/cli.py +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/aitax/components.py +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/taxforge.egg-info/SOURCES.txt +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/taxforge.egg-info/dependency_links.txt +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/taxforge.egg-info/entry_points.txt +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/taxforge.egg-info/requires.txt +0 -0
- {taxforge-0.9.16 → taxforge-0.9.18}/ui/taxforge.egg-info/top_level.txt +0 -0
|
@@ -488,13 +488,13 @@ def review_page() -> rx.Component:
|
|
|
488
488
|
rx.hstack(
|
|
489
489
|
rx.button(
|
|
490
490
|
"Select All Pending",
|
|
491
|
-
size="
|
|
491
|
+
size="1",
|
|
492
492
|
variant="outline",
|
|
493
493
|
on_click=TaxAppState.select_all_pending,
|
|
494
494
|
),
|
|
495
495
|
rx.button(
|
|
496
496
|
"Clear",
|
|
497
|
-
size="
|
|
497
|
+
size="1",
|
|
498
498
|
variant="ghost",
|
|
499
499
|
on_click=TaxAppState.clear_selection,
|
|
500
500
|
),
|
|
@@ -673,7 +673,7 @@ def review_page() -> rx.Component:
|
|
|
673
673
|
rx.text("🏠 Rental Properties (Schedule E)",
|
|
674
674
|
color=COLORS["text_primary"], font_weight="600"),
|
|
675
675
|
rx.spacer(),
|
|
676
|
-
rx.button("+ Add", size="
|
|
676
|
+
rx.button("+ Add", size="1", on_click=TaxAppState.toggle_rental_form),
|
|
677
677
|
),
|
|
678
678
|
rx.cond(
|
|
679
679
|
TaxAppState.rental_properties.length() > 0,
|
|
@@ -732,7 +732,7 @@ def review_page() -> rx.Component:
|
|
|
732
732
|
rx.text("💼 Business Income (Schedule C)",
|
|
733
733
|
color=COLORS["text_primary"], font_weight="600"),
|
|
734
734
|
rx.spacer(),
|
|
735
|
-
rx.button("+ Add", size="
|
|
735
|
+
rx.button("+ Add", size="1", on_click=TaxAppState.toggle_business_form),
|
|
736
736
|
),
|
|
737
737
|
rx.cond(
|
|
738
738
|
TaxAppState.business_income.length() > 0,
|
|
@@ -784,7 +784,7 @@ def review_page() -> rx.Component:
|
|
|
784
784
|
rx.hstack(
|
|
785
785
|
rx.text("📋 Other Income", color=COLORS["text_primary"], font_weight="600"),
|
|
786
786
|
rx.spacer(),
|
|
787
|
-
rx.button("+ Add", size="
|
|
787
|
+
rx.button("+ Add", size="1", on_click=TaxAppState.toggle_other_income_form),
|
|
788
788
|
),
|
|
789
789
|
rx.cond(
|
|
790
790
|
TaxAppState.other_income.length() > 0,
|
|
@@ -818,7 +818,7 @@ def review_page() -> rx.Component:
|
|
|
818
818
|
rx.hstack(
|
|
819
819
|
rx.text("📉 Other Deductions", color=COLORS["text_primary"], font_weight="600"),
|
|
820
820
|
rx.spacer(),
|
|
821
|
-
rx.button("+ Add", size="
|
|
821
|
+
rx.button("+ Add", size="1", on_click=TaxAppState.toggle_other_deduction_form),
|
|
822
822
|
),
|
|
823
823
|
rx.cond(
|
|
824
824
|
TaxAppState.other_deductions.length() > 0,
|
|
@@ -852,7 +852,7 @@ def review_page() -> rx.Component:
|
|
|
852
852
|
rx.hstack(
|
|
853
853
|
rx.text("👨👩👧👦 Dependents", color=COLORS["text_primary"], font_weight="600"),
|
|
854
854
|
rx.spacer(),
|
|
855
|
-
rx.button("+ Add", size="
|
|
855
|
+
rx.button("+ Add", size="1", on_click=TaxAppState.toggle_dependent_form),
|
|
856
856
|
),
|
|
857
857
|
rx.cond(
|
|
858
858
|
TaxAppState.dependents.length() > 0,
|
|
@@ -65,9 +65,29 @@ For 1099-SA (HSA distributions), extract:
|
|
|
65
65
|
- distribution: Box 1 - Gross distribution
|
|
66
66
|
- earnings: Box 2 - Earnings on excess contributions
|
|
67
67
|
|
|
68
|
+
For 1099-NEC (Nonemployee Compensation), extract:
|
|
69
|
+
- payer_name: Company/person who paid you
|
|
70
|
+
- payer_ein: Payer's TIN (if shown)
|
|
71
|
+
- nonemployee_compensation: Box 1 - Nonemployee compensation (this is self-employment income)
|
|
72
|
+
- federal_withheld: Box 4 - Federal income tax withheld (if any)
|
|
73
|
+
- state_tax_withheld: Box 5 - State tax withheld (if any)
|
|
74
|
+
|
|
75
|
+
For 1099-MISC (Miscellaneous Income), extract:
|
|
76
|
+
- payer_name: Company/person who paid you
|
|
77
|
+
- rents: Box 1 - Rents received
|
|
78
|
+
- royalties: Box 2 - Royalties
|
|
79
|
+
- other_income: Box 3 - Other income
|
|
80
|
+
- fishing_boat_proceeds: Box 5 - Fishing boat proceeds
|
|
81
|
+
- medical_payments: Box 6 - Medical and health care payments
|
|
82
|
+
- nonemployee_compensation: Box 7 - Nonemployee compensation (pre-2020 forms, now on 1099-NEC)
|
|
83
|
+
- substitute_payments: Box 8 - Substitute payments in lieu of dividends
|
|
84
|
+
- crop_insurance: Box 9 - Crop insurance proceeds
|
|
85
|
+
- attorney_fees: Box 10 - Gross proceeds paid to an attorney
|
|
86
|
+
- federal_withheld: Box 4 - Federal income tax withheld
|
|
87
|
+
|
|
68
88
|
Respond ONLY with a valid JSON object containing:
|
|
69
89
|
{
|
|
70
|
-
"document_type": "W-2" | "1099-INT" | "1099-DIV" | "1099-B" | "1098" | "5498-SA" | "1099-SA" | "OTHER",
|
|
90
|
+
"document_type": "W-2" | "1099-INT" | "1099-DIV" | "1099-B" | "1098" | "5498-SA" | "1099-SA" | "1099-NEC" | "1099-MISC" | "OTHER",
|
|
71
91
|
"confidence": 0.0-1.0,
|
|
72
92
|
"extracted_data": {
|
|
73
93
|
// relevant fields based on document type
|
|
@@ -296,3 +316,78 @@ def convert_to_1099(extracted_data: dict, form_type: str) -> dict:
|
|
|
296
316
|
"amount": amount,
|
|
297
317
|
"form_type": form_type,
|
|
298
318
|
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def convert_to_business_income(extracted_data: dict, source_type: str) -> dict:
|
|
322
|
+
"""
|
|
323
|
+
Convert 1099-NEC or 1099-MISC to Schedule C business income.
|
|
324
|
+
|
|
325
|
+
1099-NEC Box 1 = Self-employment income (goes to Schedule C)
|
|
326
|
+
1099-MISC Box 7 (pre-2020) = Same as 1099-NEC
|
|
327
|
+
"""
|
|
328
|
+
gross_income = 0.0
|
|
329
|
+
payer_name = extracted_data.get("payer_name", "Unknown Client")
|
|
330
|
+
|
|
331
|
+
if source_type == "1099-NEC":
|
|
332
|
+
gross_income = float(extracted_data.get("nonemployee_compensation", 0))
|
|
333
|
+
elif source_type == "1099-MISC":
|
|
334
|
+
# Box 7 was nonemployee comp before 2020, now rarely used
|
|
335
|
+
gross_income = float(extracted_data.get("nonemployee_compensation", 0))
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
"name": f"Self-Employment ({payer_name})",
|
|
339
|
+
"gross_income": gross_income,
|
|
340
|
+
"expenses": 0.0, # User can edit to add expenses
|
|
341
|
+
"net_profit": gross_income,
|
|
342
|
+
"source_form": source_type,
|
|
343
|
+
"payer_name": payer_name,
|
|
344
|
+
"federal_withheld": float(extracted_data.get("federal_withheld", 0)),
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def convert_1099_misc_to_other_income(extracted_data: dict) -> list:
|
|
349
|
+
"""
|
|
350
|
+
Convert 1099-MISC non-self-employment boxes to other income entries.
|
|
351
|
+
|
|
352
|
+
Returns a list of income entries for: rents, royalties, other income, etc.
|
|
353
|
+
"""
|
|
354
|
+
entries = []
|
|
355
|
+
payer = extracted_data.get("payer_name", "Unknown Payer")
|
|
356
|
+
|
|
357
|
+
# Box 1 - Rents (goes to Schedule E if rental property, otherwise other income)
|
|
358
|
+
rents = float(extracted_data.get("rents", 0))
|
|
359
|
+
if rents > 0:
|
|
360
|
+
entries.append({
|
|
361
|
+
"description": f"Rental Income - {payer} (1099-MISC Box 1)",
|
|
362
|
+
"amount": rents,
|
|
363
|
+
"category": "rental",
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
# Box 2 - Royalties (goes to Schedule E)
|
|
367
|
+
royalties = float(extracted_data.get("royalties", 0))
|
|
368
|
+
if royalties > 0:
|
|
369
|
+
entries.append({
|
|
370
|
+
"description": f"Royalties - {payer} (1099-MISC Box 2)",
|
|
371
|
+
"amount": royalties,
|
|
372
|
+
"category": "royalty",
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
# Box 3 - Other income (taxable, goes to Schedule 1 Line 8)
|
|
376
|
+
other = float(extracted_data.get("other_income", 0))
|
|
377
|
+
if other > 0:
|
|
378
|
+
entries.append({
|
|
379
|
+
"description": f"Other Income - {payer} (1099-MISC Box 3)",
|
|
380
|
+
"amount": other,
|
|
381
|
+
"category": "other",
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
# Box 10 - Attorney fees (gross proceeds)
|
|
385
|
+
attorney = float(extracted_data.get("attorney_fees", 0))
|
|
386
|
+
if attorney > 0:
|
|
387
|
+
entries.append({
|
|
388
|
+
"description": f"Attorney Fees - {payer} (1099-MISC Box 10)",
|
|
389
|
+
"amount": attorney,
|
|
390
|
+
"category": "other",
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
return entries
|
|
@@ -7,7 +7,14 @@ from pathlib import Path
|
|
|
7
7
|
from typing import List, Dict
|
|
8
8
|
from enum import Enum
|
|
9
9
|
|
|
10
|
-
from .document_extractor import
|
|
10
|
+
from .document_extractor import (
|
|
11
|
+
extract_with_retry,
|
|
12
|
+
convert_to_w2,
|
|
13
|
+
convert_to_1099,
|
|
14
|
+
convert_to_business_income,
|
|
15
|
+
convert_1099_misc_to_other_income,
|
|
16
|
+
REQUEST_DELAY_SECONDS
|
|
17
|
+
)
|
|
11
18
|
import asyncio
|
|
12
19
|
|
|
13
20
|
|
|
@@ -208,6 +215,7 @@ class TaxAppState(rx.State):
|
|
|
208
215
|
taxable_income: float = 0.0
|
|
209
216
|
total_tax: float = 0.0
|
|
210
217
|
total_withholding: float = 0.0
|
|
218
|
+
other_withholding: float = 0.0 # From 1099-NEC/MISC Box 4 (goes to Line 25c)
|
|
211
219
|
refund_or_owed: float = 0.0
|
|
212
220
|
is_refund: bool = True
|
|
213
221
|
|
|
@@ -435,6 +443,62 @@ class TaxAppState(rx.State):
|
|
|
435
443
|
"source_file": filename,
|
|
436
444
|
})
|
|
437
445
|
parsed_count += 1
|
|
446
|
+
|
|
447
|
+
elif ai_doc_type == "1099-NEC":
|
|
448
|
+
# Nonemployee compensation → Schedule C (self-employment)
|
|
449
|
+
business_data = convert_to_business_income(extracted, "1099-NEC")
|
|
450
|
+
business_data["source_file"] = filename
|
|
451
|
+
self.business_income.append(business_data)
|
|
452
|
+
|
|
453
|
+
# Track any withholding
|
|
454
|
+
withheld = float(extracted.get("federal_withheld", 0))
|
|
455
|
+
if withheld > 0:
|
|
456
|
+
# Add to a special 1099 withholding tracker (goes to Line 25c)
|
|
457
|
+
if not hasattr(self, 'other_withholding'):
|
|
458
|
+
self.other_withholding = 0.0
|
|
459
|
+
self.other_withholding += withheld
|
|
460
|
+
parsed_count += 1
|
|
461
|
+
|
|
462
|
+
elif ai_doc_type == "1099-MISC":
|
|
463
|
+
# Check if it has nonemployee comp (Box 7, pre-2020)
|
|
464
|
+
nec_amount = float(extracted.get("nonemployee_compensation", 0))
|
|
465
|
+
if nec_amount > 0:
|
|
466
|
+
# Treat like 1099-NEC
|
|
467
|
+
business_data = convert_to_business_income(extracted, "1099-MISC")
|
|
468
|
+
business_data["source_file"] = filename
|
|
469
|
+
self.business_income.append(business_data)
|
|
470
|
+
|
|
471
|
+
# Handle other 1099-MISC income (rents, royalties, other)
|
|
472
|
+
other_entries = convert_1099_misc_to_other_income(extracted)
|
|
473
|
+
for entry in other_entries:
|
|
474
|
+
entry["source_file"] = filename
|
|
475
|
+
if entry.get("category") == "rental":
|
|
476
|
+
# Add as simple rental (user can expand)
|
|
477
|
+
self.rental_properties.append({
|
|
478
|
+
"address": entry["description"],
|
|
479
|
+
"rent_income": entry["amount"],
|
|
480
|
+
"mortgage_interest": 0,
|
|
481
|
+
"property_tax": 0,
|
|
482
|
+
"insurance": 0,
|
|
483
|
+
"repairs": 0,
|
|
484
|
+
"management": 0,
|
|
485
|
+
"utilities": 0,
|
|
486
|
+
"depreciation": 0,
|
|
487
|
+
"other_expenses": 0,
|
|
488
|
+
"total_expenses": 0,
|
|
489
|
+
"net_income": entry["amount"],
|
|
490
|
+
"source_file": filename,
|
|
491
|
+
})
|
|
492
|
+
else:
|
|
493
|
+
# Other income
|
|
494
|
+
self.other_income.append({
|
|
495
|
+
"description": entry["description"],
|
|
496
|
+
"amount": entry["amount"],
|
|
497
|
+
"source_file": filename,
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
if nec_amount > 0 or other_entries:
|
|
501
|
+
parsed_count += 1
|
|
438
502
|
|
|
439
503
|
# Recalculate totals
|
|
440
504
|
self._recalculate()
|
|
@@ -456,14 +520,22 @@ class TaxAppState(rx.State):
|
|
|
456
520
|
lower = filename.lower()
|
|
457
521
|
if "w2" in lower or "w-2" in lower:
|
|
458
522
|
return "W-2"
|
|
459
|
-
elif "1099-
|
|
523
|
+
elif "1099-nec" in lower or "1099nec" in lower:
|
|
524
|
+
return "1099-NEC"
|
|
525
|
+
elif "1099-misc" in lower or "1099misc" in lower:
|
|
526
|
+
return "1099-MISC"
|
|
527
|
+
elif "1099-int" in lower or "1099int" in lower:
|
|
460
528
|
return "1099-INT"
|
|
461
|
-
elif "1099-div" in lower:
|
|
529
|
+
elif "1099-div" in lower or "1099div" in lower:
|
|
462
530
|
return "1099-DIV"
|
|
531
|
+
elif "1099-b" in lower or "1099b" in lower:
|
|
532
|
+
return "1099-B"
|
|
463
533
|
elif "1099" in lower:
|
|
464
534
|
return "1099"
|
|
465
535
|
elif "1098" in lower:
|
|
466
536
|
return "1098"
|
|
537
|
+
elif "5498" in lower:
|
|
538
|
+
return "5498-SA"
|
|
467
539
|
return "Unknown"
|
|
468
540
|
|
|
469
541
|
def add_w2(self, employer: str, wages: float, withheld: float):
|
|
@@ -783,6 +855,7 @@ class TaxAppState(rx.State):
|
|
|
783
855
|
# Withholding from all sources
|
|
784
856
|
self.total_withholding = sum(w.get("federal_withheld", 0) for w in self.w2_list)
|
|
785
857
|
self.total_withholding += sum(f.get("federal_withheld", 0) for f in self.form_1099_list)
|
|
858
|
+
self.total_withholding += self.other_withholding # From 1099-NEC/MISC
|
|
786
859
|
|
|
787
860
|
# Line 2b: Taxable interest (1099-INT)
|
|
788
861
|
self.total_interest = sum(
|
|
@@ -1060,6 +1133,7 @@ class TaxAppState(rx.State):
|
|
|
1060
1133
|
self.niit = 0.0
|
|
1061
1134
|
self.total_tax = 0.0
|
|
1062
1135
|
self.total_withholding = 0.0
|
|
1136
|
+
self.other_withholding = 0.0
|
|
1063
1137
|
self.refund_or_owed = 0.0
|
|
1064
1138
|
self.is_refund = True
|
|
1065
1139
|
self.return_status = "not_started"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|