Revert "Fix PDF generation with simplified HTML"

This reverts commit d0654af339.
This commit is contained in:
2026-03-23 11:18:30 +01:00
parent d0654af339
commit deb49aaac7

View File

@@ -14,68 +14,145 @@ pub fn generate_customer_pdf(
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let html = format!( let html = format!(
r#"<!DOCTYPE html> r#"<!DOCTYPE html>
<html> <html lang="sv">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Faktura - Kund {}</title>
<style> <style>
body {{ font-family: Arial; font-size: 10pt; margin: 20px; }} @page {{
h1 {{ font-size: 14pt; margin: 0; }} size: A4;
h2 {{ font-size: 11pt; margin: 15px 0 5px 0; border-bottom: 1px solid #000; }} margin: 15mm;
table {{ width: 100%; border-collapse: collapse; font-size: 9pt; margin-top: 5px; }} }}
th {{ background-color: #ddd; text-align: left; padding: 3px; border: 1px solid #000; }} * {{ box-sizing: border-box; }}
td {{ padding: 3px; border: 1px solid #000; }} body {{
.r {{ text-align: right; }} font-family: Arial, sans-serif;
.b {{ font-weight: bold; border-top: 2px solid #000; background-color: #eee; }} font-size: 12px;
.header {{ border-bottom: 2px solid #000; padding-bottom: 10px; margin-bottom: 15px; }} margin: 0;
.meta {{ font-size: 8pt; color: #666; }} padding: 20px;
.section {{ margin-bottom: 10px; border: 1px solid #ccc; }} }}
.section-header {{ background-color: #eee; padding: 5px; font-weight: bold; }} .header {{
.section-meta {{ padding: 3px 5px; font-size: 8pt; background-color: #f9f9f9; }} display: flex;
.grand {{ background-color: #000; color: #fff; padding: 8px; font-size: 12pt; text-align: right; font-weight: bold; margin-top: 15px; }} justify-content: space-between;
border-bottom: 2px solid #333;
padding-bottom: 10px;
margin-bottom: 15px;
}}
.header h1 {{
margin: 0;
font-size: 20px;
}}
.header .meta {{
text-align: right;
font-size: 11px;
color: #666;
}}
.summary {{
background: #f5f5f5;
border: 1px solid #ccc;
padding: 12px;
margin-bottom: 15px;
}}
.summary h2 {{
margin: 0 0 10px 0;
font-size: 14px;
}}
table {{
width: 100%;
border-collapse: collapse;
font-size: 11px;
}}
th, td {{
padding: 5px 8px;
text-align: left;
}}
th {{
background: #e0e0e0;
font-weight: bold;
}}
.grand-total-row td {{
font-weight: bold;
border-top: 2px solid #333;
background: #f0f0f0;
}}
.card-section {{
margin-bottom: 15px;
border: 1px solid #ccc;
}}
.card-header {{
background: #f0f0f0;
padding: 8px 12px;
font-weight: bold;
}}
.card-summary {{
padding: 6px 12px;
font-size: 11px;
background: #fafafa;
}}
.grand-total {{
background: #333;
color: white;
padding: 12px 15px;
font-size: 14px;
font-weight: bold;
text-align: right;
}}
</style> </style>
</head> </head>
<body> <body>
<div class="header"> <div class="header">
<h1>Faktura - Kund {customer_num}</h1> <div>
<div class="meta">Period: {period} | Genererad: {gen_date}</div> <h1>Faktura - Kund {}</h1>
<div>Period: {}</div>
</div>
<div class="meta">
<div>Genererad: {}</div>
</div>
</div> </div>
<div class="summary">
<h2>Sammanfattning</h2> <h2>Sammanfattning</h2>
<table> <table>
<tr><th>Produkt</th><th class="r">Volym (L)</th><th class="r">Belopp</th><th class="r">Snittpris/L</th></tr> <thead>
{products} <tr>
<tr class="b"><td>Totalt</td><td class="r">{total_vol}</td><td class="r">{total_amt} Kr</td><td></td></tr> <th>Produkt</th>
<th style="text-align:right">Volym (L)</th>
<th style="text-align:right">Belopp</th>
<th style="text-align:right">Snittpris/L</th>
</tr>
</thead>
<tbody>
{}
<tr class="grand-total-row">
<td>Totalt</td>
<td style="text-align:right">{}</td>
<td style="text-align:right">{} Kr</td>
<td></td>
</tr>
</tbody>
</table> </table>
</div>
{cards} {}
<div class="grand">Totalsumma: {grand_total} Kr</div>
</body> </body>
</html>"#, </html>"#,
customer_num = customer.customer_number, customer.customer_number,
period = period, customer.customer_number,
gen_date = generated_date, period,
products = generate_products_table(&customer.summary.products), generated_date,
total_vol = customer.summary.total_volume, generate_product_rows(&customer.summary.products),
total_amt = customer.summary.grand_total, customer.summary.total_volume,
cards = generate_cards(&customer.cards), customer.summary.grand_total,
grand_total = customer.summary.grand_total, generate_card_sections(&customer.cards),
); );
let mut warnings = Vec::new(); let mut warnings = Vec::new();
let doc = match PdfDocument::from_html( let doc = PdfDocument::from_html(
&html, &html,
&BTreeMap::new(), &BTreeMap::new(),
&BTreeMap::new(), &BTreeMap::new(),
&GeneratePdfOptions::default(), &GeneratePdfOptions::default(),
&mut warnings, &mut warnings,
) { )?;
Ok(d) => d,
Err(e) => {
eprintln!("PDF generation warning: {}", e);
return Err(e.into());
}
};
let file = File::create(output_path)?; let file = File::create(output_path)?;
let mut writer = BufWriter::new(file); let mut writer = BufWriter::new(file);
@@ -85,49 +162,83 @@ td {{ padding: 3px; border: 1px solid #000; }}
Ok(()) Ok(())
} }
fn generate_products_table(products: &[crate::ProductSummary]) -> String { fn generate_product_rows(products: &[crate::ProductSummary]) -> String {
products products
.iter() .iter()
.map(|p| format!( .map(|p| {
"<tr><td>{}</td><td class=\"r\">{}</td><td class=\"r\">{} Kr</td><td class=\"r\">{} Kr</td></tr>", format!(
r#"<tr>
<td>{}</td>
<td style="text-align:right">{}</td>
<td style="text-align:right">{} Kr</td>
<td style="text-align:right">{} Kr</td>
</tr>"#,
p.name, p.volume, p.amount, p.avg_price p.name, p.volume, p.amount, p.avg_price
)) )
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n ") .join("\n ")
} }
fn generate_cards(cards: &[crate::CardData]) -> String { fn generate_card_sections(cards: &[crate::CardData]) -> String {
cards cards
.iter() .iter()
.map(|card| format!( .map(|card| {
r#"<div class="section"> format!(
<div class="section-header">Kort: {} ({} transaktioner)</div> r#"<div class="card-section">
<div class="section-meta">Summa: {} Kr | Volym: {} L</div> <div class="card-header">Kort: {} | {} transaktioner</div>
<div class="card-summary">Summa: {} Kr | Volym: {} L</div>
<table> <table>
<tr><th>Datum</th><th>Produkt</th><th class="r">Pris/L</th><th class="r">Volym</th><th class="r">Belopp</th><th>Kvitto</th></tr> <thead>
<tr>
<th>Datum</th>
<th>Produkt</th>
<th style="text-align:right">Pris/L</th>
<th style="text-align:right">Volym</th>
<th style="text-align:right">Belopp</th>
<th>Kvitto</th>
</tr>
</thead>
<tbody>
{} {}
<tr class="b"><td colspan="3">Kortsumma</td><td class="r">{}</td><td class="r">{}</td><td></td></tr> <tr style="font-weight:bold;background:#f9f9f9">
<td colspan="3">Kortsumma</td>
<td style="text-align:right">{}</td>
<td style="text-align:right">{}</td>
<td></td>
</tr>
</tbody>
</table> </table>
</div>"#, </div>"#,
card.card_number, card.card_number,
card.transactions.len(), card.transactions.len(),
card.total_amount, card.total_amount,
card.total_volume, card.total_volume,
generate_transactions(&card.transactions), generate_transaction_rows(&card.transactions),
card.total_volume, card.total_volume,
card.total_amount, card.total_amount,
)) )
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n") .join("\n\n ")
} }
fn generate_transactions(txs: &[crate::FormattedTransaction]) -> String { fn generate_transaction_rows(transactions: &[crate::FormattedTransaction]) -> String {
txs transactions
.iter() .iter()
.map(|tx| format!( .map(|tx| {
"<tr><td>{}</td><td>{}</td><td class=\"r\">{}</td><td class=\"r\">{}</td><td class=\"r\">{}</td><td>{}</td></tr>", format!(
r#"<tr>
<td>{}</td>
<td>{}</td>
<td style="text-align:right">{}</td>
<td style="text-align:right">{}</td>
<td style="text-align:right">{}</td>
<td>{}</td>
</tr>"#,
tx.date, tx.quality_name, tx.price, tx.volume, tx.amount, tx.receipt tx.date, tx.quality_name, tx.price, tx.volume, tx.amount, tx.receipt
)) )
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n ") .join("\n ")
} }