OverviewFilesCommitsIssuesDocsReleasesPeers

other_accounting / ashes / Finances / Articulate Research Finances / 1065_filings / fill_1065_all_years.py

282 lines · python ref:
#!/usr/bin/env python3
"""
Fill Form 1065 + Schedule K-1 for Articulate Research LLC
Tax years 2022, 2023, 2024, 2025

Partnership: Articulate Research LLC (EIN 83-0868324)
Partners: Alexis Mattos (50%) + Jason Krueger (50%)
All years: $0 income, losses from franchise tax + EIDL interest
"""

import subprocess
import os

OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__))

PARTNERSHIP = {
    "name": "ARTICULATE RESEARCH LLC",
    "ein": "83-0868324",
    "address": "140 9TH ST",
    "city_state_zip": "SAN FRANCISCO, CA 94103",
    "business_activity": "SOFTWARE",
    "product_service": "DATA CONSULTING",
    "business_code": "541519",
    "date_started": "01/23/2017",
}

PARTNERS = [
    {"name": "Alexis K Mattos", "ssn": "606-07-1293", "address": "423 Main St, Greensboro Bend, VT 05842", "pct": 51},
    {"name": "Jason Krueger", "ssn": "391-04-1752", "address": "423 Main St, Greensboro Bend, VT 05842", "pct": 49},
]

YEAR_DATA = {
    2022: {"income": 0, "franchise_tax": 800, "eidl_interest": 305, "other_deductions": 216, "other_desc": "Google Workspace"},
    2023: {"income": 0, "franchise_tax": 800, "eidl_interest": 305, "other_deductions": 0, "other_desc": ""},
    2024: {"income": 0, "franchise_tax": 800, "eidl_interest": 305, "other_deductions": 0, "other_desc": ""},
    2025: {"income": 0, "franchise_tax": 800, "eidl_interest": 306, "other_deductions": 0, "other_desc": ""},
}


def fill_pdf(input_pdf, output_pdf, fields):
    xfdf = '<?xml version="1.0" encoding="UTF-8"?>\n'
    xfdf += '<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">\n<fields>\n'
    for k, v in fields.items():
        v_str = str(v).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
        xfdf += f'  <field name="{k}"><value>{v_str}</value></field>\n'
    xfdf += '</fields>\n</xfdf>\n'
    xfdf_path = output_pdf.replace('.pdf', '.xfdf')
    with open(xfdf_path, 'w') as f:
        f.write(xfdf)
    result = subprocess.run(['pdftk', input_pdf, 'fill_form', xfdf_path, 'output', output_pdf, 'flatten'],
                          capture_output=True, text=True)
    if result.returncode != 0:
        print(f"  ERROR: {result.stderr}")
        return False
    print(f"  OK: {os.path.basename(output_pdf)}")
    return True


def fill_1065(year):
    data = YEAR_DATA[year]
    total_deductions = data["franchise_tax"] + data["eidl_interest"] + data["other_deductions"]
    ordinary_loss = data["income"] - total_deductions  # negative = loss

    P1 = "topmostSubform[0].Page1[0]"

    fields = {
        # Header
        f"{P1}.Pg1Header[0].f1_04[0]": PARTNERSHIP["name"],
        f"{P1}.Pg1Header[0].f1_05[0]": PARTNERSHIP["address"],
        f"{P1}.Pg1Header[0].f1_06[0]": PARTNERSHIP["city_state_zip"],
        f"{P1}.Pg1Header[0].f1_10[0]": PARTNERSHIP["ein"],
        f"{P1}.Pg1Header[0].f1_11[0]": PARTNERSHIP["date_started"],
        f"{P1}.ABC[0].f1_07[0]": PARTNERSHIP["business_activity"],
        f"{P1}.ABC[0].f1_08[0]": PARTNERSHIP["product_service"],
        f"{P1}.ABC[0].f1_09[0]": PARTNERSHIP["business_code"],

        # Checkboxes
        # H: Cash accounting method
        f"{P1}.c1_6[0]": "1",
        # I: Number of K-1s = 2
        f"{P1}.f1_14[0]": "2",

        # Income section
        f"{P1}.f1_17[0]": "0",  # Line 1c: balance (gross receipts - returns)
        f"{P1}.f1_19[0]": "0",  # Line 3: gross profit
        f"{P1}.f1_24[0]": "0",  # Line 8: total income

        # Deductions
        f"{P1}.f1_30[0]": str(data["franchise_tax"]),   # Line 14: taxes and licenses
        f"{P1}.f1_31[0]": str(data["eidl_interest"]),    # Line 15: interest
    }

    if data["other_deductions"] > 0:
        fields[f"{P1}.f1_38[0]"] = str(data["other_deductions"])  # Line 20: other deductions

    fields[f"{P1}.f1_39[0]"] = str(total_deductions)     # Line 21: total deductions
    fields[f"{P1}.f1_40[0]"] = str(ordinary_loss)         # Line 22: ordinary business income (loss)

    input_pdf = os.path.join(OUTPUT_DIR, f"f1065_{year}.pdf")
    output_pdf = os.path.join(OUTPUT_DIR, f"1065_FILLED_{year}.pdf")
    return fill_pdf(input_pdf, output_pdf, fields)


def fill_k1(year, partner_idx):
    data = YEAR_DATA[year]
    total_deductions = data["franchise_tax"] + data["eidl_interest"] + data["other_deductions"]
    ordinary_loss = data["income"] - total_deductions
    partner = PARTNERS[partner_idx]
    pct = partner["pct"]
    partner_share = int(ordinary_loss * pct / 100)  # 51/49 split
    P1 = "topmostSubform[0].Page1[0]"

    fields = {
        # Header - calendar year
        f"{P1}.Pg1Header[0].ForCalendarYear[0].f1_1[0]": str(year),

        # Part I - Partnership info
        f"{P1}.LeftCol[0].f1_6[0]": PARTNERSHIP["ein"],
        f"{P1}.LeftCol[0].f1_7[0]": f"{PARTNERSHIP['name']}\n{PARTNERSHIP['address']}\n{PARTNERSHIP['city_state_zip']}",

        # Part II - Partner info
        f"{P1}.LeftCol[0].f1_9[0]": partner["ssn"],
        f"{P1}.LeftCol[0].f1_10[0]": f"{partner['name']}\n{partner['address']}",

        # G: General partner checkbox
        f"{P1}.LeftCol[0].c1_4[0]": "1",
        # H1: Domestic partner
        f"{P1}.LeftCol[0].c1_5[0]": "1",

        # J: Partner's share of profit, loss, capital
        f"{P1}.LeftCol[0].LineJTable[0].Profit[0].f1_14[0]": str(pct),
        f"{P1}.LeftCol[0].LineJTable[0].Profit[0].f1_15[0]": str(pct),
        f"{P1}.LeftCol[0].LineJTable[0].Loss[0].f1_16[0]": str(pct),
        f"{P1}.LeftCol[0].LineJTable[0].Loss[0].f1_17[0]": str(pct),
        f"{P1}.LeftCol[0].LineJTable[0].Capital[0].f1_18[0]": str(pct),
        f"{P1}.LeftCol[0].LineJTable[0].Capital[0].f1_19[0]": str(pct),

        # I1: type of entity
        f"{P1}.LeftCol[0].f1_13[0]": "INDIVIDUAL",

        # Part III - Line 1: Ordinary business income (loss) - CORRECT FIELD PATH
        f"{P1}.RightCol[0].RightCol1[0].f1_34[0]": str(partner_share),
    }

    partner_label = "alexis" if partner_idx == 0 else "jason"
    input_pdf = os.path.join(OUTPUT_DIR, f"f1065sk1_{year}.pdf")
    output_pdf = os.path.join(OUTPUT_DIR, f"K1_{partner_label}_{year}.pdf")
    return fill_pdf(input_pdf, output_pdf, fields)


def create_waiver_statement():
    statement = """PENALTY RELIEF REQUESTED UNDER REVENUE PROCEDURE 84-35

This partnership had 2 partners during the tax year, both natural
persons (U.S. citizens). All partnership items were allocated
proportionately (51/49). Each partner timely filed their individual
income tax return and reported their full share of all partnership items.

Partnership: Articulate Research LLC
EIN: 83-0868324
Partners: Alexis K Mattos (51%), Jason Krueger (49%)

Signed under penalty of perjury,


_________________________________     _______________
Alexis Mattos, Member                 Date


Citation: Revenue Procedure 84-35, 1984-1 C.B. 509
Confirmed applicable post-2018: IRS PMTA 2020-01
"""
    path = os.path.join(OUTPUT_DIR, "rev_proc_84-35_waiver_statement.txt")
    with open(path, 'w') as f:
        f.write(statement)
    print(f"  OK: {os.path.basename(path)}")


def create_filing_instructions():
    instructions = """FILING INSTRUCTIONS - ARTICULATE RESEARCH LLC (Form 1065)
=========================================================

FOR EACH YEAR (2022, 2023, 2024, 2025):

WHAT TO MAIL (in this order):
  1. Rev. Proc. 84-35 penalty waiver statement (printed, signed)
  2. Form 1065 (partnership return)
  3. Schedule K-1 for Alexis Mattos (Copy A - for IRS)
  4. Schedule K-1 for Jason Krueger (Copy A - for IRS)

ALSO GIVE TO EACH PARTNER:
  - Copy B of their K-1 (for their personal tax return)
  - Alexis reports her K-1 loss on Schedule E, Part II
  - Jason reports his K-1 loss on Schedule E, Part II

WHERE TO MAIL (all 4 years go to the same address):
  Department of the Treasury
  Internal Revenue Service Center
  Kansas City, MO 64999-0045

  (Note: this is a different address than the 1040 address)

HOW TO MAIL:
  - Paper file only (prior years cannot be e-filed)
  - Send CERTIFIED MAIL with return receipt requested
  - You can mail all 4 years in separate envelopes in the same trip
  - Each year should be in its own envelope

ALSO FILE WITH CALIFORNIA (Form 568 for each year):
  Franchise Tax Board
  PO Box 942857
  Sacramento, CA 94257-0501

SIGN EACH RETURN:
  - Alexis signs page 1 of each Form 1065 ("Sign Here" section)
  - Sign the waiver statement for each year
  - Date each signature

KEEP COPIES OF EVERYTHING.

JASON'S AMENDMENTS:
  Jason needs to file Form 1040-X for 2022, 2023, and 2024 to add
  Schedule E showing his K-1 losses. For 2025, include the K-1 on
  his original return.

  2022 amendment deadline: April 15, 2026 (URGENT)
  2023 amendment deadline: April 15, 2027
  2024 amendment deadline: April 15, 2028
"""
    path = os.path.join(OUTPUT_DIR, "1065_filing_instructions.txt")
    with open(path, 'w') as f:
        f.write(instructions)
    print(f"  OK: {os.path.basename(path)}")


def merge_year(year):
    files = [
        os.path.join(OUTPUT_DIR, f"1065_FILLED_{year}.pdf"),
        os.path.join(OUTPUT_DIR, f"K1_alexis_{year}.pdf"),
        os.path.join(OUTPUT_DIR, f"K1_jason_{year}.pdf"),
    ]
    existing = [f for f in files if os.path.exists(f)]
    output = os.path.join(OUTPUT_DIR, f"1065_COMPLETE_{year}.pdf")
    result = subprocess.run(['pdftk'] + existing + ['cat', 'output', output], capture_output=True, text=True)
    if result.returncode == 0:
        print(f"  OK: 1065_COMPLETE_{year}.pdf")
    else:
        print(f"  ERROR merge {year}: {result.stderr}")


if __name__ == "__main__":
    print("=" * 60)
    print("Form 1065 - Articulate Research LLC")
    print("EIN: 83-0868324 | 50/50 Split")
    print("Alexis Mattos + Jason Krueger")
    print("=" * 60)

    for year in [2022, 2023, 2024, 2025]:
        data = YEAR_DATA[year]
        total_ded = data["franchise_tax"] + data["eidl_interest"] + data["other_deductions"]
        loss = data["income"] - total_ded
        print(f"\n--- {year} (income: $0, deductions: ${total_ded}, loss: ${loss}) ---")

        fill_1065(year)
        fill_k1(year, 0)  # alexis
        fill_k1(year, 1)  # jason
        merge_year(year)

    print("\n--- Supporting documents ---")
    create_waiver_statement()
    create_filing_instructions()

    print("\n" + "=" * 60)
    print("FILES GENERATED:")
    for year in [2022, 2023, 2024, 2025]:
        print(f"  1065_COMPLETE_{year}.pdf (1065 + both K-1s)")
    print("  rev_proc_84-35_waiver_statement.txt")
    print("  1065_filing_instructions.txt")
    print("\nPrint the waiver statement, sign it, and attach to")
    print("the FRONT of each year's 1065 before mailing.")
    print("=" * 60)