Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions backend/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2069,7 +2069,10 @@ pub async fn get_plan_report(
}
};

// 4. Build PDF data structs (owned, so they can cross the thread boundary)
// 4. Compute projected yield (real-time accrued yield since last ping)
let projected_yield = compute_projected_accrued_yield(&plan);

// 5. Build PDF data structs (owned, so they can cross the thread boundary)
let report_data = crate::pdf::PlanReportData {
plan_id: plan.id.to_string(),
owner_address: plan.owner_address.clone(),
Expand All @@ -2079,6 +2082,7 @@ pub async fn get_plan_report(
earn_yield: plan.earn_yield,
yield_rate_bps: plan.yield_rate_bps,
accrued_yield: plan.accrued_yield.to_string(),
projected_accrued_yield: format!("{projected_yield:.7}"),
created_at: plan.created_at.to_rfc3339(),
grace_period_seconds: plan.grace_period_seconds,
beneficiaries: beneficiary_rows
Expand All @@ -2098,7 +2102,7 @@ pub async fn get_plan_report(
.collect(),
};

// 5. Generate PDF in a blocking thread (printpdf is CPU-bound / not async)
// 6. Generate PDF in a blocking thread (printpdf is CPU-bound / not async)
let pdf_bytes =
match tokio::task::spawn_blocking(move || crate::pdf::generate(report_data)).await {
Ok(Ok(bytes)) => bytes,
Expand All @@ -2118,7 +2122,7 @@ pub async fn get_plan_report(
}
};

// 6. Return the PDF as a downloadable attachment
// 7. Return the PDF as a downloadable attachment
let filename = format!("plan-{plan_id}-report.pdf");
(
StatusCode::OK,
Expand Down
37 changes: 29 additions & 8 deletions backend/src/pdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct PlanReportData {
pub earn_yield: bool,
pub yield_rate_bps: i32,
pub accrued_yield: String,
pub projected_accrued_yield: String,
pub created_at: String,
pub grace_period_seconds: i64,
pub beneficiaries: Vec<BeneficiaryData>,
Expand Down Expand Up @@ -67,37 +68,57 @@ pub fn generate(data: PlanReportData) -> Result<Vec<u8>, String> {
9.0,
format!("Owner: {}", data.owner_address)
);
line!(regular, 9.0, format!("Status: {}", data.status));
line!(
regular,
9.0,
format!("Token: {}", data.token_address)
format!("Grace Period (s): {}", data.grace_period_seconds)
);
line!(regular, 9.0, format!("Amount: {}", data.amount));
line!(regular, 9.0, format!("Status: {}", data.status));
line!(
regular,
9.0,
format!("Earn Yield: {}", data.earn_yield)
format!("Created At: {}", data.created_at)
);
y -= section_gap;

// Assets
line!(bold, 11.0, "Assets");
line!(
regular,
9.0,
format!("Token Address: {}", data.token_address)
);
line!(regular, 9.0, format!("Locked Amount: {}", data.amount));
y -= section_gap;

// Yield Details
let apy_pct = data.yield_rate_bps as f32 / 100.0;
line!(bold, 11.0, "Yield Details");
line!(
regular,
9.0,
format!("Yield Rate (bps): {}", data.yield_rate_bps)
format!("Earn Yield: {}", data.earn_yield)
);
line!(
regular,
9.0,
format!("Accrued Yield: {}", data.accrued_yield)
format!(
"Yield Rate: {} bps ({:.2}% APY)",
data.yield_rate_bps, apy_pct
)
);
line!(
regular,
9.0,
format!("Grace Period (s): {}", data.grace_period_seconds)
format!("Accrued Yield: {} (snapshotted)", data.accrued_yield)
);
line!(
regular,
9.0,
format!("Created At: {}", data.created_at)
format!(
"Projected Yield: {} (real-time)",
data.projected_accrued_yield
)
);
y -= section_gap;

Expand Down
Loading