pub fn write_csv_exports(
out_dir: &Path,
state: &LedgerState,
tax_year: Option<i32>,
se_result: Option<&SeTaxResult>,
donation_details: &BTreeMap<EventId, DonationDetails>,
) -> Result<(), CliError>Expand description
FR10: write the projected ledger as CSV (the NFR2 plaintext exception). One row per disposal/removal
leg (flattened) + one per lot/income record. Exact values (Decimal/i64) as strings (NFR5).
Each CSV is opened via fsperms::open_owner_only (0o600 on Unix) so decrypted PII matches the
hardened permissions already applied to snapshot.sqlite by the store crate. The out-dir is
created owner-only (0o700) if absent; when the dir PRE-EXISTS, open_owner_only still forces 0o600
on each new CSV file (the hole that Writer::from_path + umask would leave).
The lots/disposals/removals/income CSVs are all-years (a full projection dump). The
Form 8949 + Schedule D filing artifacts are inherently per-tax-year (P2-B): when tax_year
is Some(y), form8949.csv + schedule_d.csv are additionally written, year-scoped to y;
when None, they are omitted.