taxforge 0.9.15__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """TaxForge - AI-powered tax preparation"""
2
2
 
3
- __version__ = "0.9.15"
3
+ __version__ = "0.9.16"
aitax/aitax.py CHANGED
@@ -847,6 +847,69 @@ def review_page() -> rx.Component:
847
847
  background=COLORS["bg_hover"], border_radius="8px",
848
848
  ),
849
849
 
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
+ ),
912
+
850
913
  spacing="4",
851
914
  width="100%",
852
915
  ),
aitax/cli.py CHANGED
@@ -8,7 +8,7 @@ import shutil
8
8
  from pathlib import Path
9
9
 
10
10
 
11
- __version__ = "0.9.15"
11
+ __version__ = "0.9.16"
12
12
 
13
13
  RXCONFIG_CONTENT = '''"""Reflex config for TaxForge."""
14
14
  import reflex as rx
aitax/state.py CHANGED
@@ -136,11 +136,18 @@ class TaxAppState(rx.State):
136
136
  other_deduction_form_desc: str = ""
137
137
  other_deduction_form_amount: str = ""
138
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
+
139
145
  # Form visibility
140
146
  show_rental_form: bool = False
141
147
  show_business_form: bool = False
142
148
  show_other_income_form: bool = False
143
149
  show_other_deduction_form: bool = False
150
+ show_dependent_form: bool = False
144
151
 
145
152
  # ===== Document State =====
146
153
  uploaded_files: List[str] = []
@@ -168,6 +175,12 @@ class TaxAppState(rx.State):
168
175
  other_income: List[Dict] = [] # {description, amount, tax_type}
169
176
  other_deductions: List[Dict] = [] # {description, amount, deduction_type}
170
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
+
171
184
  # ===== Calculated Values =====
172
185
  total_wages: float = 0.0
173
186
  total_interest: float = 0.0
@@ -186,6 +199,12 @@ class TaxAppState(rx.State):
186
199
  capital_gains_tax: float = 0.0 # Separate tax on capital gains
187
200
  additional_medicare_tax: float = 0.0 # 0.9% on wages > $250k (MFJ)
188
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
189
208
  taxable_income: float = 0.0
190
209
  total_tax: float = 0.0
191
210
  total_withholding: float = 0.0
@@ -555,6 +574,28 @@ class TaxAppState(rx.State):
555
574
  self.other_deductions.pop(index)
556
575
  self._recalculate()
557
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
+
558
599
  # ===== Form Toggle Methods =====
559
600
  def toggle_rental_form(self):
560
601
  self.show_rental_form = not self.show_rental_form
@@ -568,6 +609,9 @@ class TaxAppState(rx.State):
568
609
  def toggle_other_deduction_form(self):
569
610
  self.show_other_deduction_form = not self.show_other_deduction_form
570
611
 
612
+ def toggle_dependent_form(self):
613
+ self.show_dependent_form = not self.show_dependent_form
614
+
571
615
  # ===== Form Input Setters =====
572
616
  def set_rental_address(self, val): self.rental_form_address = val
573
617
  def set_rental_income(self, val): self.rental_form_income = val
@@ -587,6 +631,11 @@ class TaxAppState(rx.State):
587
631
  def set_other_deduction_desc(self, val): self.other_deduction_form_desc = val
588
632
  def set_other_deduction_amount(self, val): self.other_deduction_form_amount = val
589
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
+
590
639
  # ===== Form Submit Methods =====
591
640
  def submit_rental_form(self):
592
641
  """Submit rental property form."""
@@ -659,6 +708,34 @@ class TaxAppState(rx.State):
659
708
  except ValueError:
660
709
  self.error_message = "Invalid amount"
661
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
+
662
739
  def remove_file(self, filename: str):
663
740
  """Remove an uploaded file and its extracted data."""
664
741
  if filename in self.uploaded_files:
@@ -871,10 +948,51 @@ class TaxAppState(rx.State):
871
948
  niit_base = min(investment_income, max(0, self.adjusted_gross_income - niit_threshold))
872
949
  self.niit = niit_base * 0.038
873
950
 
874
- # Total tax = ordinary + capital gains + additional medicare + NIIT + SE tax
875
- self.total_tax = (ordinary_tax + self.capital_gains_tax +
876
- self.additional_medicare_tax + self.niit +
877
- self.self_employment_tax)
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)
878
996
 
879
997
  # ===== REFUND OR AMOUNT OWED =====
880
998
  self.refund_or_owed = self.total_withholding - self.total_tax
@@ -919,6 +1037,8 @@ class TaxAppState(rx.State):
919
1037
  self.business_income = []
920
1038
  self.other_income = []
921
1039
  self.other_deductions = []
1040
+ self.dependents = []
1041
+ self.child_care_expenses = 0.0
922
1042
  self.form_1099_list = []
923
1043
  self.total_wages = 0.0
924
1044
  self.total_interest = 0.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taxforge
3
- Version: 0.9.15
3
+ Version: 0.9.16
4
4
  Summary: AI-powered tax preparation assistant
5
5
  Author: TaxForge Team
6
6
  License: MIT
@@ -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,,
@@ -1,11 +0,0 @@
1
- aitax/__init__.py,sha256=JncLlRPr8j0rLn9Z0IqBYPMdBa-gb059WY-BvS1oRyU,68
2
- aitax/aitax.py,sha256=e4DitXQwj61BtgVTaV-so8U00P6EKkec6LKVxbkFdHE,59433
3
- aitax/cli.py,sha256=bbkhzRJyrne-0p3aImWuQCUDbzOOBV-4aEg-cviVxIg,2651
4
- aitax/components.py,sha256=pi4wKOtNsw0ju1cN3ivY7UWF0Q32rshijeklFnxVt5U,21056
5
- aitax/document_extractor.py,sha256=l2bMajEwK382lH57eiHOn5E1ohgijezgldiBURiV4_s,10459
6
- aitax/state.py,sha256=Xj9NU237CT6eOHAT91NPDJeNOZESa9yqtc8Q7SfTerg,37016
7
- taxforge-0.9.15.dist-info/METADATA,sha256=AzyV0f1f_erkAHyllM3Wx_N_4WVYHO_qGh0_I2ENeek,4874
8
- taxforge-0.9.15.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
- taxforge-0.9.15.dist-info/entry_points.txt,sha256=2dG4U_m3yvQaVeD8LeEGm1v8szyNGQtXUZOvbwH75Gg,44
10
- taxforge-0.9.15.dist-info/top_level.txt,sha256=H7DuLZSRzSwHT5STc7uQa0oTOsKcNYUz2Pn4a4C5Q0Y,6
11
- taxforge-0.9.15.dist-info/RECORD,,