#![allow(dead_code)]
use btctax_core::tax::packet::{assemble_printed_return, PrintedReturn};
use btctax_core::tax::return_1040::{assemble_absolute, AbsoluteReturn};
use btctax_core::tax::testonly::{
build_golden_household, ty2024_params, ty2024_table, GoldenHousehold,
};
use btctax_core::{
Form8949Box, Form8949Part, Form8949Row, ScheduleDPart, ScheduleDTotals, WalletId,
};
use btctax_forms::{fill_full_return, NamedForm};
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use std::collections::BTreeMap;
use time::macros::date;
pub fn row(
part: Form8949Part,
desc: &str,
proceeds: Decimal,
cost: Decimal,
exchange: bool,
) -> Form8949Row {
Form8949Row {
part,
box_: if part == Form8949Part::ShortTerm {
Form8949Box::C
} else {
Form8949Box::F
},
box_needs_review: exchange,
description: desc.to_string(),
date_acquired: date!(2024 - 02 - 03),
date_sold: date!(2025 - 06 - 15),
proceeds,
cost_basis: cost,
adjustment_code: String::new(),
adjustment_amount: dec!(0),
gain: proceeds - cost,
wallet: if exchange {
WalletId::Exchange {
provider: "acme".into(),
account: "a1".into(),
}
} else {
WalletId::SelfCustody {
label: "cold".into(),
}
},
disposition_kind: btctax_core::DisposeKind::Sell,
}
}
pub fn mixed_rows() -> Vec<Form8949Row> {
vec![
row(
Form8949Part::ShortTerm,
"0.53000000 BTC",
dec!(30000.50),
dec!(25000),
false,
),
row(
Form8949Part::ShortTerm,
"0.10000000 BTC",
dec!(6000),
dec!(5500),
true,
),
row(
Form8949Part::LongTerm,
"1.00000000 BTC",
dec!(60000),
dec!(20000),
false,
),
]
}
pub fn sum_part(rows: &[Form8949Row], part: Form8949Part) -> ScheduleDPart {
let mut p = ScheduleDPart::default();
for r in rows.iter().filter(|r| r.part == part) {
p.proceeds += r.proceeds;
p.cost_basis += r.cost_basis;
p.gain += r.gain;
}
p
}
pub fn totals_for(rows: &[Form8949Row]) -> ScheduleDTotals {
ScheduleDTotals {
st: sum_part(rows, Form8949Part::ShortTerm),
lt: sum_part(rows, Form8949Part::LongTerm),
}
}
pub struct FullReturn {
pub ar: AbsoluteReturn,
pub pr: PrintedReturn,
pub forms: Vec<NamedForm>,
}
pub fn full_return(h: &GoldenHousehold) -> FullReturn {
let (ri, state) = build_golden_household(h);
let params = ty2024_params();
let table = ty2024_table();
let ar = assemble_absolute(&ri, &state, ¶ms, &table, 2024);
let pr = assemble_printed_return(&ri, &state, &BTreeMap::new(), &ar, &table, 2024)
.expect("the golden households carry well-formed SSNs");
let forms =
fill_full_return(&pr, 2024).unwrap_or_else(|e| panic!("{}: the packet must fill — {e}", h.name));
FullReturn { ar, pr, forms }
}
pub fn packet(h: &GoldenHousehold) -> Vec<NamedForm> {
full_return(h).forms
}
pub fn form<'a>(pkt: &'a [NamedForm], name: &str) -> &'a NamedForm {
pkt.iter()
.find(|f| f.name == name)
.unwrap_or_else(|| panic!("the packet is missing {name}"))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Sign {
Leading,
ParenMagnitude,
Unsigned,
}
pub fn on_paper_signed(cells: &BTreeMap<String, String>, key: &str, sign: Sign) -> Option<i64> {
let raw = cells.get(key)?;
let value: i64 = raw.parse().unwrap_or_else(|_| {
panic!("on_paper_signed: cell {key:?} is present but not a parseable integer: {raw:?}")
});
Some(match sign {
Sign::Leading | Sign::Unsigned => value,
Sign::ParenMagnitude => -value,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Blank {
PresentZero,
AbsentIsZero,
}
pub fn cell_or_zero(cells: &BTreeMap<String, String>, key: &str, regime: Blank) -> i64 {
match regime {
Blank::PresentZero => {
let raw = cells.get(key).unwrap_or_else(|| {
panic!(
"cell_or_zero: PresentZero requires {key:?} to be present-and-\"0\", but it is \
absent — the filler stopped writing the line this guard depends on"
)
});
assert_eq!(
raw, "0",
"cell_or_zero: PresentZero requires {key:?} == \"0\", got {raw:?}"
);
0
}
Blank::AbsentIsZero => match cells.get(key) {
None => 0,
Some(raw) => raw.parse().unwrap_or_else(|_| {
panic!("cell_or_zero: cell {key:?} is present but not a parseable integer: {raw:?}")
}),
},
}
}