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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """TaxForge - AI-powered tax preparation"""
2
2
 
3
- __version__ = "0.9.14"
3
+ __version__ = "0.9.16"
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.text("🏠 Rental Properties (Schedule E)",
673
- color=COLORS["text_primary"], font_weight="600"),
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.text("💼 Business Income (Schedule C)",
698
- color=COLORS["text_primary"], font_weight="600"),
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.text("📋 Other Income", color=COLORS["text_primary"], font_weight="600"),
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.text("📉 Other Deductions (Above-the-line)", color=COLORS["text_primary"], font_weight="600"),
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
- rx.text("💡 Use the API or console to add entries. UI forms coming soon.",
768
- color=COLORS["text_muted"], font_size="12px", font_style="italic"),
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
@@ -8,7 +8,7 @@ import shutil
8
8
  from pathlib import Path
9
9
 
10
10
 
11
- __version__ = "0.9.14"
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
@@ -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
- # Total tax = ordinary + capital gains + additional medicare + NIIT + SE tax
743
- self.total_tax = (ordinary_tax + self.capital_gains_tax +
744
- self.additional_medicare_tax + self.niit +
745
- 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)
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taxforge
3
- Version: 0.9.14
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=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,,