use crate::conventions::TaxDate;
use crate::forms::Form8283Section;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DonationDetails {
pub donee_name: String,
#[serde(default)]
pub donee_address: Option<String>,
#[serde(default)]
pub donee_ein: Option<String>,
pub appraiser_name: String,
#[serde(default)]
pub appraiser_address: Option<String>,
#[serde(default)]
pub appraiser_tin: Option<String>,
#[serde(default)]
pub appraiser_ptin: Option<String>,
#[serde(default)]
pub appraiser_qualifications: Option<String>,
#[serde(default)]
pub appraisal_date: Option<TaxDate>,
#[serde(default)]
pub fmv_method_override: Option<String>,
}
impl DonationDetails {
pub fn is_review_complete(&self, section: Form8283Section) -> bool {
match section {
Form8283Section::B => {
!self.appraiser_name.is_empty()
&& (self.appraiser_tin.is_some() || self.appraiser_ptin.is_some())
&& self.appraisal_date.is_some()
&& self.appraiser_qualifications.is_some()
&& self.donee_ein.is_some()
}
Form8283Section::A => true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use time::macros::date;
fn full_section_b() -> DonationDetails {
DonationDetails {
donee_name: "Test Charity".into(),
donee_address: Some("123 Main St, Anytown USA".into()),
donee_ein: Some("12-3456789".into()),
appraiser_name: "Test Appraiser".into(),
appraiser_address: Some("456 Appraiser Ave".into()),
appraiser_tin: Some("987-65-4321".into()),
appraiser_ptin: Some("P01234567".into()),
appraiser_qualifications: Some("Certified bitcoin appraiser, 10 yrs exp".into()),
appraisal_date: Some(date!(2025 - 06 - 01)),
fmv_method_override: None,
}
}
fn skeletal() -> DonationDetails {
DonationDetails {
donee_name: "Test Charity".into(),
donee_address: None,
donee_ein: None,
appraiser_name: "Test Appraiser".into(),
appraiser_address: None,
appraiser_tin: None,
appraiser_ptin: None,
appraiser_qualifications: None,
appraisal_date: None,
fmv_method_override: None,
}
}
#[test]
fn section_b_full_details_is_complete() {
assert!(full_section_b().is_review_complete(Form8283Section::B));
}
#[test]
fn section_b_skeletal_is_not_complete() {
assert!(!skeletal().is_review_complete(Form8283Section::B));
}
#[test]
fn section_b_missing_tin_and_ptin_is_not_complete() {
let mut d = full_section_b();
d.appraiser_tin = None;
d.appraiser_ptin = None;
assert!(!d.is_review_complete(Form8283Section::B));
}
#[test]
fn section_b_ptin_alone_satisfies_tin_or_ptin() {
let mut d = full_section_b();
d.appraiser_tin = None; assert!(d.is_review_complete(Form8283Section::B));
}
#[test]
fn section_b_tin_alone_satisfies_tin_or_ptin() {
let mut d = full_section_b();
d.appraiser_ptin = None; assert!(d.is_review_complete(Form8283Section::B));
}
#[test]
fn section_b_missing_donee_ein_is_not_complete() {
let mut d = full_section_b();
d.donee_ein = None;
assert!(!d.is_review_complete(Form8283Section::B));
}
#[test]
fn section_b_missing_appraisal_date_is_not_complete() {
let mut d = full_section_b();
d.appraisal_date = None;
assert!(!d.is_review_complete(Form8283Section::B));
}
#[test]
fn section_b_missing_qualifications_is_not_complete() {
let mut d = full_section_b();
d.appraiser_qualifications = None;
assert!(!d.is_review_complete(Form8283Section::B));
}
#[test]
fn section_a_skeletal_is_complete() {
assert!(skeletal().is_review_complete(Form8283Section::A));
}
#[test]
fn section_a_full_details_is_complete() {
assert!(full_section_b().is_review_complete(Form8283Section::A));
}
}