#!/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('&', '&').replace('<', '<').replace('>', '>')
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)