Add PDF generation for invoices

- Generate both HTML and PDF versions of each customer invoice
- PDFs use printpdf with HTML rendering for consistent styling
- Same layout and formatting as HTML output
This commit is contained in:
2026-03-23 11:04:24 +01:00
parent 4119ad7059
commit 0ebadbdb48
3 changed files with 281 additions and 26 deletions

View File

@@ -6,6 +6,7 @@ use std::fs;
use std::path::Path;
mod invoice_generator;
mod pdf;
use invoice_generator::{group_by_customer, read_csv_file, Customer};
@@ -14,43 +15,43 @@ fn fmt(v: f64) -> String {
}
#[derive(Clone)]
struct ProductSummary {
name: String,
volume: String,
amount: String,
avg_price: String,
pub struct ProductSummary {
pub name: String,
pub volume: String,
pub amount: String,
pub avg_price: String,
}
#[derive(Clone)]
struct Summary {
total_volume: String,
grand_total: String,
products: Vec<ProductSummary>,
pub struct Summary {
pub total_volume: String,
pub grand_total: String,
pub products: Vec<ProductSummary>,
}
#[derive(Clone)]
struct CardData {
card_number: String,
transactions: Vec<FormattedTransaction>,
total_amount: String,
total_volume: String,
pub struct CardData {
pub card_number: String,
pub transactions: Vec<FormattedTransaction>,
pub total_amount: String,
pub total_volume: String,
}
#[derive(Clone)]
struct FormattedTransaction {
date: String,
quality_name: String,
price: String,
volume: String,
amount: String,
receipt: String,
pub struct FormattedTransaction {
pub date: String,
pub quality_name: String,
pub price: String,
pub volume: String,
pub amount: String,
pub receipt: String,
}
#[derive(Clone)]
struct PreparedCustomer {
customer_number: String,
cards: Vec<CardData>,
summary: Summary,
pub struct PreparedCustomer {
pub customer_number: String,
pub cards: Vec<CardData>,
pub summary: Summary,
}
impl PreparedCustomer {
@@ -212,7 +213,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
for (customer_num, customer) in customers {
let prepared = PreparedCustomer::from_customer(customer);
let customer_html = CustomerTemplate {
customer: prepared,
customer: prepared.clone(),
period: period.clone(),
generated_date: generated_date.clone(),
}
@@ -221,6 +222,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let filename = format!("customer_{}.html", customer_num);
fs::write(output_dir.join(&filename), customer_html)?;
println!("Genererade {}", filename);
let pdf_filename = format!("customer_{}.pdf", customer_num);
pdf::generate_customer_pdf(
&prepared,
&period,
&generated_date,
&output_dir.join(&pdf_filename),
)?;
println!("Genererade {}", pdf_filename);
}
println!(