taxforge 0.9.14__py3-none-any.whl → 0.9.16__py3-none-any.whl
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.
- aitax/__init__.py +1 -1
- aitax/aitax.py +151 -8
- aitax/cli.py +1 -1
- aitax/state.py +256 -4
- {taxforge-0.9.14.dist-info → taxforge-0.9.16.dist-info}/METADATA +1 -1
- taxforge-0.9.16.dist-info/RECORD +11 -0
- taxforge-0.9.14.dist-info/RECORD +0 -11
- {taxforge-0.9.14.dist-info → taxforge-0.9.16.dist-info}/WHEEL +0 -0
- {taxforge-0.9.14.dist-info → taxforge-0.9.16.dist-info}/entry_points.txt +0 -0
- {taxforge-0.9.14.dist-info → taxforge-0.9.16.dist-info}/top_level.txt +0 -0
aitax/__init__.py
CHANGED
aitax/aitax.py
CHANGED
|
@@ -669,8 +669,12 @@ def review_page() -> rx.Component:
|
|
|
669
669
|
|
|
670
670
|
# Rental Property (Schedule E)
|
|
671
671
|
rx.vstack(
|
|
672
|
-
rx.
|
|
673
|
-
|
|
672
|
+
rx.hstack(
|
|
673
|
+
rx.text("🏠 Rental Properties (Schedule E)",
|
|
674
|
+
color=COLORS["text_primary"], font_weight="600"),
|
|
675
|
+
rx.spacer(),
|
|
676
|
+
rx.button("+ Add", size="sm", on_click=TaxAppState.toggle_rental_form),
|
|
677
|
+
),
|
|
674
678
|
rx.cond(
|
|
675
679
|
TaxAppState.rental_properties.length() > 0,
|
|
676
680
|
rx.foreach(
|
|
@@ -686,6 +690,36 @@ def review_page() -> rx.Component:
|
|
|
686
690
|
),
|
|
687
691
|
rx.text("No rental properties added", color=COLORS["text_muted"], font_style="italic"),
|
|
688
692
|
),
|
|
693
|
+
# Rental Form
|
|
694
|
+
rx.cond(
|
|
695
|
+
TaxAppState.show_rental_form,
|
|
696
|
+
rx.vstack(
|
|
697
|
+
rx.input(placeholder="Address", value=TaxAppState.rental_form_address, on_change=TaxAppState.set_rental_address, width="100%"),
|
|
698
|
+
rx.hstack(
|
|
699
|
+
rx.input(placeholder="Rent Income", value=TaxAppState.rental_form_income, on_change=TaxAppState.set_rental_income, type="number"),
|
|
700
|
+
rx.input(placeholder="Mortgage Int", value=TaxAppState.rental_form_mortgage, on_change=TaxAppState.set_rental_mortgage, type="number"),
|
|
701
|
+
width="100%",
|
|
702
|
+
),
|
|
703
|
+
rx.hstack(
|
|
704
|
+
rx.input(placeholder="Property Tax", value=TaxAppState.rental_form_tax, on_change=TaxAppState.set_rental_tax, type="number"),
|
|
705
|
+
rx.input(placeholder="Insurance", value=TaxAppState.rental_form_insurance, on_change=TaxAppState.set_rental_insurance, type="number"),
|
|
706
|
+
width="100%",
|
|
707
|
+
),
|
|
708
|
+
rx.hstack(
|
|
709
|
+
rx.input(placeholder="Repairs", value=TaxAppState.rental_form_repairs, on_change=TaxAppState.set_rental_repairs, type="number"),
|
|
710
|
+
rx.input(placeholder="Depreciation", value=TaxAppState.rental_form_depreciation, on_change=TaxAppState.set_rental_depreciation, type="number"),
|
|
711
|
+
width="100%",
|
|
712
|
+
),
|
|
713
|
+
rx.input(placeholder="Other Expenses", value=TaxAppState.rental_form_other, on_change=TaxAppState.set_rental_other, type="number", width="100%"),
|
|
714
|
+
rx.hstack(
|
|
715
|
+
rx.button("Cancel", variant="outline", on_click=TaxAppState.toggle_rental_form),
|
|
716
|
+
rx.button("Add Property", on_click=TaxAppState.submit_rental_form),
|
|
717
|
+
width="100%", justify="end",
|
|
718
|
+
),
|
|
719
|
+
width="100%", spacing="2", padding="12px",
|
|
720
|
+
background="white", border_radius="8px", border=f"1px solid {COLORS['border']}",
|
|
721
|
+
),
|
|
722
|
+
),
|
|
689
723
|
rx.text(f"Total Rental Income: ${TaxAppState.total_rental_income:,.2f}",
|
|
690
724
|
color=COLORS["success"], font_size="13px"),
|
|
691
725
|
width="100%", spacing="2", padding="12px",
|
|
@@ -694,8 +728,12 @@ def review_page() -> rx.Component:
|
|
|
694
728
|
|
|
695
729
|
# Business Income (Schedule C)
|
|
696
730
|
rx.vstack(
|
|
697
|
-
rx.
|
|
698
|
-
|
|
731
|
+
rx.hstack(
|
|
732
|
+
rx.text("💼 Business Income (Schedule C)",
|
|
733
|
+
color=COLORS["text_primary"], font_weight="600"),
|
|
734
|
+
rx.spacer(),
|
|
735
|
+
rx.button("+ Add", size="sm", on_click=TaxAppState.toggle_business_form),
|
|
736
|
+
),
|
|
699
737
|
rx.cond(
|
|
700
738
|
TaxAppState.business_income.length() > 0,
|
|
701
739
|
rx.foreach(
|
|
@@ -711,6 +749,25 @@ def review_page() -> rx.Component:
|
|
|
711
749
|
),
|
|
712
750
|
rx.text("No businesses added", color=COLORS["text_muted"], font_style="italic"),
|
|
713
751
|
),
|
|
752
|
+
# Business Form
|
|
753
|
+
rx.cond(
|
|
754
|
+
TaxAppState.show_business_form,
|
|
755
|
+
rx.vstack(
|
|
756
|
+
rx.input(placeholder="Business Name", value=TaxAppState.business_form_name, on_change=TaxAppState.set_business_name, width="100%"),
|
|
757
|
+
rx.hstack(
|
|
758
|
+
rx.input(placeholder="Gross Income", value=TaxAppState.business_form_income, on_change=TaxAppState.set_business_income, type="number"),
|
|
759
|
+
rx.input(placeholder="Total Expenses", value=TaxAppState.business_form_expenses, on_change=TaxAppState.set_business_expenses, type="number"),
|
|
760
|
+
width="100%",
|
|
761
|
+
),
|
|
762
|
+
rx.hstack(
|
|
763
|
+
rx.button("Cancel", variant="outline", on_click=TaxAppState.toggle_business_form),
|
|
764
|
+
rx.button("Add Business", on_click=TaxAppState.submit_business_form),
|
|
765
|
+
width="100%", justify="end",
|
|
766
|
+
),
|
|
767
|
+
width="100%", spacing="2", padding="12px",
|
|
768
|
+
background="white", border_radius="8px", border=f"1px solid {COLORS['border']}",
|
|
769
|
+
),
|
|
770
|
+
),
|
|
714
771
|
rx.text(f"Total Business Income: ${TaxAppState.total_business_income:,.2f}",
|
|
715
772
|
color=COLORS["success"], font_size="13px"),
|
|
716
773
|
rx.cond(
|
|
@@ -724,7 +781,11 @@ def review_page() -> rx.Component:
|
|
|
724
781
|
|
|
725
782
|
# Other Income
|
|
726
783
|
rx.vstack(
|
|
727
|
-
rx.
|
|
784
|
+
rx.hstack(
|
|
785
|
+
rx.text("📋 Other Income", color=COLORS["text_primary"], font_weight="600"),
|
|
786
|
+
rx.spacer(),
|
|
787
|
+
rx.button("+ Add", size="sm", on_click=TaxAppState.toggle_other_income_form),
|
|
788
|
+
),
|
|
728
789
|
rx.cond(
|
|
729
790
|
TaxAppState.other_income.length() > 0,
|
|
730
791
|
rx.foreach(
|
|
@@ -739,13 +800,26 @@ def review_page() -> rx.Component:
|
|
|
739
800
|
),
|
|
740
801
|
rx.text("No other income added", color=COLORS["text_muted"], font_style="italic"),
|
|
741
802
|
),
|
|
803
|
+
rx.cond(
|
|
804
|
+
TaxAppState.show_other_income_form,
|
|
805
|
+
rx.hstack(
|
|
806
|
+
rx.input(placeholder="Description", value=TaxAppState.other_income_form_desc, on_change=TaxAppState.set_other_income_desc, flex="1"),
|
|
807
|
+
rx.input(placeholder="Amount", value=TaxAppState.other_income_form_amount, on_change=TaxAppState.set_other_income_amount, type="number", width="120px"),
|
|
808
|
+
rx.button("Add", on_click=TaxAppState.submit_other_income_form),
|
|
809
|
+
width="100%",
|
|
810
|
+
),
|
|
811
|
+
),
|
|
742
812
|
width="100%", spacing="2", padding="12px",
|
|
743
813
|
background=COLORS["bg_hover"], border_radius="8px",
|
|
744
814
|
),
|
|
745
815
|
|
|
746
816
|
# Other Deductions
|
|
747
817
|
rx.vstack(
|
|
748
|
-
rx.
|
|
818
|
+
rx.hstack(
|
|
819
|
+
rx.text("📉 Other Deductions", color=COLORS["text_primary"], font_weight="600"),
|
|
820
|
+
rx.spacer(),
|
|
821
|
+
rx.button("+ Add", size="sm", on_click=TaxAppState.toggle_other_deduction_form),
|
|
822
|
+
),
|
|
749
823
|
rx.cond(
|
|
750
824
|
TaxAppState.other_deductions.length() > 0,
|
|
751
825
|
rx.foreach(
|
|
@@ -760,12 +834,81 @@ def review_page() -> rx.Component:
|
|
|
760
834
|
),
|
|
761
835
|
rx.text("No deductions added", color=COLORS["text_muted"], font_style="italic"),
|
|
762
836
|
),
|
|
837
|
+
rx.cond(
|
|
838
|
+
TaxAppState.show_other_deduction_form,
|
|
839
|
+
rx.hstack(
|
|
840
|
+
rx.input(placeholder="Description", value=TaxAppState.other_deduction_form_desc, on_change=TaxAppState.set_other_deduction_desc, flex="1"),
|
|
841
|
+
rx.input(placeholder="Amount", value=TaxAppState.other_deduction_form_amount, on_change=TaxAppState.set_other_deduction_amount, type="number", width="120px"),
|
|
842
|
+
rx.button("Add", on_click=TaxAppState.submit_other_deduction_form),
|
|
843
|
+
width="100%",
|
|
844
|
+
),
|
|
845
|
+
),
|
|
763
846
|
width="100%", spacing="2", padding="12px",
|
|
764
847
|
background=COLORS["bg_hover"], border_radius="8px",
|
|
765
848
|
),
|
|
766
849
|
|
|
767
|
-
|
|
768
|
-
|
|
850
|
+
# Dependents & Child Tax Credit
|
|
851
|
+
rx.vstack(
|
|
852
|
+
rx.hstack(
|
|
853
|
+
rx.text("👨👩👧👦 Dependents", color=COLORS["text_primary"], font_weight="600"),
|
|
854
|
+
rx.spacer(),
|
|
855
|
+
rx.button("+ Add", size="sm", on_click=TaxAppState.toggle_dependent_form),
|
|
856
|
+
),
|
|
857
|
+
rx.cond(
|
|
858
|
+
TaxAppState.dependents.length() > 0,
|
|
859
|
+
rx.foreach(
|
|
860
|
+
TaxAppState.dependents,
|
|
861
|
+
lambda d, idx: rx.hstack(
|
|
862
|
+
rx.text(d["name"], flex="1"),
|
|
863
|
+
rx.text(f"{d['relationship']}, Age {d['age']}"),
|
|
864
|
+
rx.badge(
|
|
865
|
+
rx.cond(d["is_child"], "CTC $2,000", "Credit $500"),
|
|
866
|
+
color_scheme=rx.cond(d["is_child"], "green", "blue"),
|
|
867
|
+
),
|
|
868
|
+
rx.icon("x", size=14, cursor="pointer",
|
|
869
|
+
on_click=lambda: TaxAppState.remove_dependent(idx)),
|
|
870
|
+
width="100%", padding="8px",
|
|
871
|
+
),
|
|
872
|
+
),
|
|
873
|
+
rx.text("No dependents added", color=COLORS["text_muted"], font_style="italic"),
|
|
874
|
+
),
|
|
875
|
+
# Dependent Form
|
|
876
|
+
rx.cond(
|
|
877
|
+
TaxAppState.show_dependent_form,
|
|
878
|
+
rx.vstack(
|
|
879
|
+
rx.hstack(
|
|
880
|
+
rx.input(placeholder="Name", value=TaxAppState.dependent_form_name, on_change=TaxAppState.set_dependent_name, flex="1"),
|
|
881
|
+
rx.input(placeholder="Relationship", value=TaxAppState.dependent_form_relationship, on_change=TaxAppState.set_dependent_relationship, width="120px"),
|
|
882
|
+
rx.input(placeholder="Age", value=TaxAppState.dependent_form_age, on_change=TaxAppState.set_dependent_age, type="number", width="80px"),
|
|
883
|
+
width="100%",
|
|
884
|
+
),
|
|
885
|
+
rx.text("Under 17 = $2,000 Child Tax Credit | 17+ = $500 Other Dependent Credit",
|
|
886
|
+
color=COLORS["text_muted"], font_size="12px"),
|
|
887
|
+
rx.hstack(
|
|
888
|
+
rx.button("Cancel", variant="outline", on_click=TaxAppState.toggle_dependent_form),
|
|
889
|
+
rx.button("Add Dependent", on_click=TaxAppState.submit_dependent_form),
|
|
890
|
+
width="100%", justify="end",
|
|
891
|
+
),
|
|
892
|
+
width="100%", spacing="2", padding="12px",
|
|
893
|
+
background="white", border_radius="8px", border=f"1px solid {COLORS['border']}",
|
|
894
|
+
),
|
|
895
|
+
),
|
|
896
|
+
# Credits Summary
|
|
897
|
+
rx.cond(
|
|
898
|
+
TaxAppState.child_tax_credit > 0,
|
|
899
|
+
rx.text(f"Child Tax Credit: ${TaxAppState.child_tax_credit:,.2f}",
|
|
900
|
+
color=COLORS["success"], font_size="13px"),
|
|
901
|
+
),
|
|
902
|
+
rx.cond(
|
|
903
|
+
TaxAppState.other_dependent_credit > 0,
|
|
904
|
+
rx.text(f"Other Dependent Credit: ${TaxAppState.other_dependent_credit:,.2f}",
|
|
905
|
+
color=COLORS["success"], font_size="13px"),
|
|
906
|
+
),
|
|
907
|
+
rx.text(f"Total Tax Credits: ${TaxAppState.total_credits:,.2f}",
|
|
908
|
+
color=COLORS["success"], font_size="13px", font_weight="600"),
|
|
909
|
+
width="100%", spacing="2", padding="12px",
|
|
910
|
+
background=COLORS["bg_hover"], border_radius="8px",
|
|
911
|
+
),
|
|
769
912
|
|
|
770
913
|
spacing="4",
|
|
771
914
|
width="100%",
|
aitax/cli.py
CHANGED
aitax/state.py
CHANGED
|
@@ -114,6 +114,41 @@ class TaxAppState(rx.State):
|
|
|
114
114
|
settings_api_key_input: str = "" # Temp input for settings page
|
|
115
115
|
settings_dirty: bool = False # Track unsaved changes
|
|
116
116
|
|
|
117
|
+
# ===== Form Inputs for Manual Entry =====
|
|
118
|
+
# Rental property form
|
|
119
|
+
rental_form_address: str = ""
|
|
120
|
+
rental_form_income: str = ""
|
|
121
|
+
rental_form_mortgage: str = ""
|
|
122
|
+
rental_form_tax: str = ""
|
|
123
|
+
rental_form_insurance: str = ""
|
|
124
|
+
rental_form_repairs: str = ""
|
|
125
|
+
rental_form_depreciation: str = ""
|
|
126
|
+
rental_form_other: str = ""
|
|
127
|
+
|
|
128
|
+
# Business form
|
|
129
|
+
business_form_name: str = ""
|
|
130
|
+
business_form_income: str = ""
|
|
131
|
+
business_form_expenses: str = ""
|
|
132
|
+
|
|
133
|
+
# Other income/deduction forms
|
|
134
|
+
other_income_form_desc: str = ""
|
|
135
|
+
other_income_form_amount: str = ""
|
|
136
|
+
other_deduction_form_desc: str = ""
|
|
137
|
+
other_deduction_form_amount: str = ""
|
|
138
|
+
|
|
139
|
+
# Dependent form
|
|
140
|
+
dependent_form_name: str = ""
|
|
141
|
+
dependent_form_relationship: str = ""
|
|
142
|
+
dependent_form_age: str = ""
|
|
143
|
+
child_care_form_amount: str = ""
|
|
144
|
+
|
|
145
|
+
# Form visibility
|
|
146
|
+
show_rental_form: bool = False
|
|
147
|
+
show_business_form: bool = False
|
|
148
|
+
show_other_income_form: bool = False
|
|
149
|
+
show_other_deduction_form: bool = False
|
|
150
|
+
show_dependent_form: bool = False
|
|
151
|
+
|
|
117
152
|
# ===== Document State =====
|
|
118
153
|
uploaded_files: List[str] = []
|
|
119
154
|
parsed_documents: List[Dict] = [] # {name, type, status: pending|processing|parsed|error}
|
|
@@ -140,6 +175,12 @@ class TaxAppState(rx.State):
|
|
|
140
175
|
other_income: List[Dict] = [] # {description, amount, tax_type}
|
|
141
176
|
other_deductions: List[Dict] = [] # {description, amount, deduction_type}
|
|
142
177
|
|
|
178
|
+
# Dependents
|
|
179
|
+
dependents: List[Dict] = [] # {name, relationship, age, ssn_last4, is_child}
|
|
180
|
+
|
|
181
|
+
# Child and Dependent Care
|
|
182
|
+
child_care_expenses: float = 0.0 # Expenses for dependent care while working
|
|
183
|
+
|
|
143
184
|
# ===== Calculated Values =====
|
|
144
185
|
total_wages: float = 0.0
|
|
145
186
|
total_interest: float = 0.0
|
|
@@ -158,6 +199,12 @@ class TaxAppState(rx.State):
|
|
|
158
199
|
capital_gains_tax: float = 0.0 # Separate tax on capital gains
|
|
159
200
|
additional_medicare_tax: float = 0.0 # 0.9% on wages > $250k (MFJ)
|
|
160
201
|
niit: float = 0.0 # Net Investment Income Tax 3.8%
|
|
202
|
+
|
|
203
|
+
# Tax Credits
|
|
204
|
+
child_tax_credit: float = 0.0 # $2,000 per child under 17
|
|
205
|
+
other_dependent_credit: float = 0.0 # $500 per other dependent
|
|
206
|
+
child_care_credit: float = 0.0 # Credit for child/dependent care
|
|
207
|
+
total_credits: float = 0.0
|
|
161
208
|
taxable_income: float = 0.0
|
|
162
209
|
total_tax: float = 0.0
|
|
163
210
|
total_withholding: float = 0.0
|
|
@@ -527,6 +574,168 @@ class TaxAppState(rx.State):
|
|
|
527
574
|
self.other_deductions.pop(index)
|
|
528
575
|
self._recalculate()
|
|
529
576
|
|
|
577
|
+
# ===== Dependent Methods =====
|
|
578
|
+
def add_dependent(self, name: str, relationship: str, age: int, is_child: bool = True):
|
|
579
|
+
"""Add a dependent (child or other)."""
|
|
580
|
+
self.dependents.append({
|
|
581
|
+
"name": name,
|
|
582
|
+
"relationship": relationship,
|
|
583
|
+
"age": age,
|
|
584
|
+
"is_child": is_child and age < 17, # Child Tax Credit requires under 17
|
|
585
|
+
})
|
|
586
|
+
self._recalculate()
|
|
587
|
+
|
|
588
|
+
def remove_dependent(self, index: int):
|
|
589
|
+
"""Remove a dependent."""
|
|
590
|
+
if 0 <= index < len(self.dependents):
|
|
591
|
+
self.dependents.pop(index)
|
|
592
|
+
self._recalculate()
|
|
593
|
+
|
|
594
|
+
def set_child_care_expenses(self, amount: float):
|
|
595
|
+
"""Set child/dependent care expenses."""
|
|
596
|
+
self.child_care_expenses = amount
|
|
597
|
+
self._recalculate()
|
|
598
|
+
|
|
599
|
+
# ===== Form Toggle Methods =====
|
|
600
|
+
def toggle_rental_form(self):
|
|
601
|
+
self.show_rental_form = not self.show_rental_form
|
|
602
|
+
|
|
603
|
+
def toggle_business_form(self):
|
|
604
|
+
self.show_business_form = not self.show_business_form
|
|
605
|
+
|
|
606
|
+
def toggle_other_income_form(self):
|
|
607
|
+
self.show_other_income_form = not self.show_other_income_form
|
|
608
|
+
|
|
609
|
+
def toggle_other_deduction_form(self):
|
|
610
|
+
self.show_other_deduction_form = not self.show_other_deduction_form
|
|
611
|
+
|
|
612
|
+
def toggle_dependent_form(self):
|
|
613
|
+
self.show_dependent_form = not self.show_dependent_form
|
|
614
|
+
|
|
615
|
+
# ===== Form Input Setters =====
|
|
616
|
+
def set_rental_address(self, val): self.rental_form_address = val
|
|
617
|
+
def set_rental_income(self, val): self.rental_form_income = val
|
|
618
|
+
def set_rental_mortgage(self, val): self.rental_form_mortgage = val
|
|
619
|
+
def set_rental_tax(self, val): self.rental_form_tax = val
|
|
620
|
+
def set_rental_insurance(self, val): self.rental_form_insurance = val
|
|
621
|
+
def set_rental_repairs(self, val): self.rental_form_repairs = val
|
|
622
|
+
def set_rental_depreciation(self, val): self.rental_form_depreciation = val
|
|
623
|
+
def set_rental_other(self, val): self.rental_form_other = val
|
|
624
|
+
|
|
625
|
+
def set_business_name(self, val): self.business_form_name = val
|
|
626
|
+
def set_business_income(self, val): self.business_form_income = val
|
|
627
|
+
def set_business_expenses(self, val): self.business_form_expenses = val
|
|
628
|
+
|
|
629
|
+
def set_other_income_desc(self, val): self.other_income_form_desc = val
|
|
630
|
+
def set_other_income_amount(self, val): self.other_income_form_amount = val
|
|
631
|
+
def set_other_deduction_desc(self, val): self.other_deduction_form_desc = val
|
|
632
|
+
def set_other_deduction_amount(self, val): self.other_deduction_form_amount = val
|
|
633
|
+
|
|
634
|
+
def set_dependent_name(self, val): self.dependent_form_name = val
|
|
635
|
+
def set_dependent_relationship(self, val): self.dependent_form_relationship = val
|
|
636
|
+
def set_dependent_age(self, val): self.dependent_form_age = val
|
|
637
|
+
def set_child_care_amount(self, val): self.child_care_form_amount = val
|
|
638
|
+
|
|
639
|
+
# ===== Form Submit Methods =====
|
|
640
|
+
def submit_rental_form(self):
|
|
641
|
+
"""Submit rental property form."""
|
|
642
|
+
try:
|
|
643
|
+
self.add_rental_property(
|
|
644
|
+
address=self.rental_form_address or "Property",
|
|
645
|
+
rent_income=float(self.rental_form_income or 0),
|
|
646
|
+
mortgage_interest=float(self.rental_form_mortgage or 0),
|
|
647
|
+
property_tax=float(self.rental_form_tax or 0),
|
|
648
|
+
insurance=float(self.rental_form_insurance or 0),
|
|
649
|
+
repairs=float(self.rental_form_repairs or 0),
|
|
650
|
+
depreciation=float(self.rental_form_depreciation or 0),
|
|
651
|
+
other_expenses=float(self.rental_form_other or 0),
|
|
652
|
+
)
|
|
653
|
+
# Clear form
|
|
654
|
+
self.rental_form_address = ""
|
|
655
|
+
self.rental_form_income = ""
|
|
656
|
+
self.rental_form_mortgage = ""
|
|
657
|
+
self.rental_form_tax = ""
|
|
658
|
+
self.rental_form_insurance = ""
|
|
659
|
+
self.rental_form_repairs = ""
|
|
660
|
+
self.rental_form_depreciation = ""
|
|
661
|
+
self.rental_form_other = ""
|
|
662
|
+
self.show_rental_form = False
|
|
663
|
+
self.success_message = "Rental property added!"
|
|
664
|
+
except ValueError:
|
|
665
|
+
self.error_message = "Invalid numbers in form"
|
|
666
|
+
|
|
667
|
+
def submit_business_form(self):
|
|
668
|
+
"""Submit business income form."""
|
|
669
|
+
try:
|
|
670
|
+
self.add_business(
|
|
671
|
+
name=self.business_form_name or "Business",
|
|
672
|
+
gross_income=float(self.business_form_income or 0),
|
|
673
|
+
expenses=float(self.business_form_expenses or 0),
|
|
674
|
+
)
|
|
675
|
+
self.business_form_name = ""
|
|
676
|
+
self.business_form_income = ""
|
|
677
|
+
self.business_form_expenses = ""
|
|
678
|
+
self.show_business_form = False
|
|
679
|
+
self.success_message = "Business added!"
|
|
680
|
+
except ValueError:
|
|
681
|
+
self.error_message = "Invalid numbers in form"
|
|
682
|
+
|
|
683
|
+
def submit_other_income_form(self):
|
|
684
|
+
"""Submit other income form."""
|
|
685
|
+
try:
|
|
686
|
+
self.add_other_income(
|
|
687
|
+
description=self.other_income_form_desc or "Other Income",
|
|
688
|
+
amount=float(self.other_income_form_amount or 0),
|
|
689
|
+
)
|
|
690
|
+
self.other_income_form_desc = ""
|
|
691
|
+
self.other_income_form_amount = ""
|
|
692
|
+
self.show_other_income_form = False
|
|
693
|
+
self.success_message = "Other income added!"
|
|
694
|
+
except ValueError:
|
|
695
|
+
self.error_message = "Invalid amount"
|
|
696
|
+
|
|
697
|
+
def submit_other_deduction_form(self):
|
|
698
|
+
"""Submit other deduction form."""
|
|
699
|
+
try:
|
|
700
|
+
self.add_other_deduction(
|
|
701
|
+
description=self.other_deduction_form_desc or "Other Deduction",
|
|
702
|
+
amount=float(self.other_deduction_form_amount or 0),
|
|
703
|
+
)
|
|
704
|
+
self.other_deduction_form_desc = ""
|
|
705
|
+
self.other_deduction_form_amount = ""
|
|
706
|
+
self.show_other_deduction_form = False
|
|
707
|
+
self.success_message = "Deduction added!"
|
|
708
|
+
except ValueError:
|
|
709
|
+
self.error_message = "Invalid amount"
|
|
710
|
+
|
|
711
|
+
def submit_dependent_form(self):
|
|
712
|
+
"""Submit dependent form."""
|
|
713
|
+
try:
|
|
714
|
+
age = int(self.dependent_form_age or 0)
|
|
715
|
+
self.add_dependent(
|
|
716
|
+
name=self.dependent_form_name or "Dependent",
|
|
717
|
+
relationship=self.dependent_form_relationship or "Child",
|
|
718
|
+
age=age,
|
|
719
|
+
is_child=(age < 17),
|
|
720
|
+
)
|
|
721
|
+
self.dependent_form_name = ""
|
|
722
|
+
self.dependent_form_relationship = ""
|
|
723
|
+
self.dependent_form_age = ""
|
|
724
|
+
self.show_dependent_form = False
|
|
725
|
+
self.success_message = "Dependent added!"
|
|
726
|
+
except ValueError:
|
|
727
|
+
self.error_message = "Invalid age"
|
|
728
|
+
|
|
729
|
+
def submit_child_care_expenses(self):
|
|
730
|
+
"""Submit child care expenses."""
|
|
731
|
+
try:
|
|
732
|
+
self.child_care_expenses = float(self.child_care_form_amount or 0)
|
|
733
|
+
self.child_care_form_amount = ""
|
|
734
|
+
self._recalculate()
|
|
735
|
+
self.success_message = "Child care expenses updated!"
|
|
736
|
+
except ValueError:
|
|
737
|
+
self.error_message = "Invalid amount"
|
|
738
|
+
|
|
530
739
|
def remove_file(self, filename: str):
|
|
531
740
|
"""Remove an uploaded file and its extracted data."""
|
|
532
741
|
if filename in self.uploaded_files:
|
|
@@ -739,10 +948,51 @@ class TaxAppState(rx.State):
|
|
|
739
948
|
niit_base = min(investment_income, max(0, self.adjusted_gross_income - niit_threshold))
|
|
740
949
|
self.niit = niit_base * 0.038
|
|
741
950
|
|
|
742
|
-
#
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
951
|
+
# Gross tax before credits
|
|
952
|
+
gross_tax = (ordinary_tax + self.capital_gains_tax +
|
|
953
|
+
self.additional_medicare_tax + self.niit +
|
|
954
|
+
self.self_employment_tax)
|
|
955
|
+
|
|
956
|
+
# ===== TAX CREDITS =====
|
|
957
|
+
|
|
958
|
+
# Child Tax Credit ($2,000 per qualifying child under 17)
|
|
959
|
+
# Phase out: MFJ $400k, others $200k
|
|
960
|
+
ctc_phaseout = 400000 if self.filing_status == "married_filing_jointly" else 200000
|
|
961
|
+
qualifying_children = sum(1 for d in self.dependents if d.get("is_child", False))
|
|
962
|
+
base_ctc = qualifying_children * 2000
|
|
963
|
+
|
|
964
|
+
# Phase out: reduce by $50 for each $1,000 over threshold
|
|
965
|
+
if self.adjusted_gross_income > ctc_phaseout:
|
|
966
|
+
reduction = ((self.adjusted_gross_income - ctc_phaseout) // 1000) * 50
|
|
967
|
+
self.child_tax_credit = max(0, base_ctc - reduction)
|
|
968
|
+
else:
|
|
969
|
+
self.child_tax_credit = base_ctc
|
|
970
|
+
|
|
971
|
+
# Credit for Other Dependents ($500 per dependent not qualifying for CTC)
|
|
972
|
+
other_dependents = sum(1 for d in self.dependents if not d.get("is_child", False))
|
|
973
|
+
self.other_dependent_credit = other_dependents * 500
|
|
974
|
+
|
|
975
|
+
# Child and Dependent Care Credit (20-35% of expenses, max $3,000/1 or $6,000/2+)
|
|
976
|
+
if self.child_care_expenses > 0 and qualifying_children > 0:
|
|
977
|
+
max_expenses = 3000 if qualifying_children == 1 else 6000
|
|
978
|
+
eligible_expenses = min(self.child_care_expenses, max_expenses)
|
|
979
|
+
# Credit rate: 35% if AGI <= $15,000, phases down to 20% at $43,000+
|
|
980
|
+
if self.adjusted_gross_income <= 15000:
|
|
981
|
+
rate = 0.35
|
|
982
|
+
elif self.adjusted_gross_income >= 43000:
|
|
983
|
+
rate = 0.20
|
|
984
|
+
else:
|
|
985
|
+
rate = 0.35 - ((self.adjusted_gross_income - 15000) // 2000) * 0.01
|
|
986
|
+
rate = max(0.20, rate)
|
|
987
|
+
self.child_care_credit = eligible_expenses * rate
|
|
988
|
+
else:
|
|
989
|
+
self.child_care_credit = 0.0
|
|
990
|
+
|
|
991
|
+
# Total credits (nonrefundable - can't exceed tax)
|
|
992
|
+
self.total_credits = self.child_tax_credit + self.other_dependent_credit + self.child_care_credit
|
|
993
|
+
|
|
994
|
+
# Total tax after credits
|
|
995
|
+
self.total_tax = max(0, gross_tax - self.total_credits)
|
|
746
996
|
|
|
747
997
|
# ===== REFUND OR AMOUNT OWED =====
|
|
748
998
|
self.refund_or_owed = self.total_withholding - self.total_tax
|
|
@@ -787,6 +1037,8 @@ class TaxAppState(rx.State):
|
|
|
787
1037
|
self.business_income = []
|
|
788
1038
|
self.other_income = []
|
|
789
1039
|
self.other_deductions = []
|
|
1040
|
+
self.dependents = []
|
|
1041
|
+
self.child_care_expenses = 0.0
|
|
790
1042
|
self.form_1099_list = []
|
|
791
1043
|
self.total_wages = 0.0
|
|
792
1044
|
self.total_interest = 0.0
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
aitax/__init__.py,sha256=l2Os-WDmdC962LQuemEbe_NvuUInlH7r3pkudOaV_cI,68
|
|
2
|
+
aitax/aitax.py,sha256=3V3LdjT5q64eZoFgdsiTqWV9aZLgWD3NUtKzBPCfQ2w,63630
|
|
3
|
+
aitax/cli.py,sha256=2HlOOXVh1rJixJbGhVGKOPtiLlLD_NzsAhrun5FLHyU,2651
|
|
4
|
+
aitax/components.py,sha256=pi4wKOtNsw0ju1cN3ivY7UWF0Q32rshijeklFnxVt5U,21056
|
|
5
|
+
aitax/document_extractor.py,sha256=l2bMajEwK382lH57eiHOn5E1ohgijezgldiBURiV4_s,10459
|
|
6
|
+
aitax/state.py,sha256=Kra1XJnlaTn8QccpVW5MlDwFtiFzBeJ679UWzSZ2zWc,42086
|
|
7
|
+
taxforge-0.9.16.dist-info/METADATA,sha256=JXe6pV9rHDxCyHjleg1XDiJKiaE8FZ7xMnww5Ropav8,4874
|
|
8
|
+
taxforge-0.9.16.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
+
taxforge-0.9.16.dist-info/entry_points.txt,sha256=2dG4U_m3yvQaVeD8LeEGm1v8szyNGQtXUZOvbwH75Gg,44
|
|
10
|
+
taxforge-0.9.16.dist-info/top_level.txt,sha256=H7DuLZSRzSwHT5STc7uQa0oTOsKcNYUz2Pn4a4C5Q0Y,6
|
|
11
|
+
taxforge-0.9.16.dist-info/RECORD,,
|
taxforge-0.9.14.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
aitax/__init__.py,sha256=omsRF8unZz7XxUWlm7O6jFvDjy8SqZLb9ppiuvrLjfc,68
|
|
2
|
-
aitax/aitax.py,sha256=-ccTlkDJGq88EyVKlr1pk9M4pXv5tRBlVfmGIvDMUZg,53471
|
|
3
|
-
aitax/cli.py,sha256=u8lThIhG8FNt3qB5GhpKJWM2FS1T-WEYVh-cSA1_hfg,2651
|
|
4
|
-
aitax/components.py,sha256=pi4wKOtNsw0ju1cN3ivY7UWF0Q32rshijeklFnxVt5U,21056
|
|
5
|
-
aitax/document_extractor.py,sha256=l2bMajEwK382lH57eiHOn5E1ohgijezgldiBURiV4_s,10459
|
|
6
|
-
aitax/state.py,sha256=EZX02N0iJfcKWnqf2RpLU9SXMhAAdeRI_rvkkPIvLSY,31458
|
|
7
|
-
taxforge-0.9.14.dist-info/METADATA,sha256=AJWOAYy_KE_bvyngZKbwXScE2gLgMKzwuSJp4NduvOg,4874
|
|
8
|
-
taxforge-0.9.14.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
9
|
-
taxforge-0.9.14.dist-info/entry_points.txt,sha256=2dG4U_m3yvQaVeD8LeEGm1v8szyNGQtXUZOvbwH75Gg,44
|
|
10
|
-
taxforge-0.9.14.dist-info/top_level.txt,sha256=H7DuLZSRzSwHT5STc7uQa0oTOsKcNYUz2Pn4a4C5Q0Y,6
|
|
11
|
-
taxforge-0.9.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|