RTIX / src /application /services /billing.rs
github-actions
deploy: clean backend production release
c33971d
use crate::domain::models::{Merchant, OrderRecord};
pub struct BillingService;
impl BillingService {
pub fn generate_invoice_html(order: &OrderRecord, merchant: &Merchant) -> String {
let subtotal = order.price_inr;
let total_tax = order.cgst + order.sgst + order.igst;
let grand_total = subtotal + total_tax + order.delivery_fee;
format!(
r#"<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Invoice - {tx_id}</title>
<style>
body {{ font-family: 'Inter', sans-serif; color: #111; margin: 0; padding: 40px; line-height: 1.5; }}
.invoice-box {{ max-width: 800px; margin: auto; border: 1px solid #eee; padding: 30px; border-radius: 8px; }}
.header {{ display: flex; justify-content: space-between; margin-bottom: 40px; border-bottom: 2px solid #000; padding-bottom: 20px; }}
.brand {{ font-size: 24px; font-weight: 800; text-transform: uppercase; letter-spacing: 2px; }}
.meta {{ text-align: right; }}
.details {{ display: grid; grid-template-columns: 1fr 1fr; gap: 40px; margin-bottom: 40px; }}
.section-title {{ font-size: 10px; font-weight: 800; color: #888; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 8px; }}
table {{ width: 100%; border-collapse: collapse; margin-bottom: 40px; }}
th {{ text-align: left; background: #fafafa; padding: 12px; font-size: 12px; text-transform: uppercase; border-bottom: 1px solid #eee; }}
td {{ padding: 12px; font-size: 14px; border-bottom: 1px solid #eee; }}
.totals {{ margin-left: auto; width: 300px; }}
.total-row {{ display: flex; justify-content: space-between; padding: 8px 0; }}
.grand-total {{ font-size: 18px; font-weight: 800; border-top: 2px solid #000; margin-top: 10px; padding-top: 10px; }}
.footer {{ margin-top: 60px; text-align: center; color: #888; font-size: 12px; }}
</style>
</head>
<body>
<div class="invoice-box">
<div class="header">
<div class="brand">{brand_name}</div>
<div class="meta">
<div style="font-weight: 700;">INVOICE</div>
<div style="font-size: 12px; color: #666;">#{tx_id}</div>
<div style="font-size: 12px; color: #666;">Date: {date}</div>
</div>
</div>
<div class="details">
<div>
<div class="section-title">Sold By</div>
<div style="font-weight: 600;">{brand_name}</div>
<div style="font-size: 13px; color: #444; max-width: 250px;">{merchant_addr}</div>
</div>
<div>
<div class="section-title">Ship To</div>
<div style="font-weight: 600;">{buyer_name}</div>
<div style="font-size: 13px; color: #444;">{buyer_email}</div>
<div style="font-size: 13px; color: #444; max-width: 250px;">{buyer_addr}</div>
</div>
</div>
<table>
<thead>
<tr>
<th>Item Description</th>
<th style="text-align: right;">Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>Product Purchase ({link_id})</td>
<td style="text-align: right;">₹{subtotal:.2}</td>
</tr>
</tbody>
</table>
<div class="totals">
<div class="total-row">
<span>Subtotal</span>
<span>₹{subtotal:.2}</span>
</div>
<div class="total-row">
<span>Tax (GST)</span>
<span>₹{total_tax:.2}</span>
</div>
<div class="total-row">
<span>Shipping</span>
<span>₹{shipping:.2}</span>
</div>
{discount_row}
<div class="total-row grand-total">
<span>Grand Total</span>
<span>₹{total:.2}</span>
</div>
</div>
<div class="footer">
Generated by Rtix Sovereign Settlement Protocol. Secure Transaction Verified.
</div>
</div>
</body>
</html>"#,
brand_name = merchant.brand_name,
tx_id = order.transaction_id,
date = order
.created_at
.map(|t| t.format("%d %b %Y").to_string())
.unwrap_or_else(|| "N/A".to_string()),
merchant_addr = merchant.business_address.as_deref().unwrap_or("N/A"),
buyer_name = order.buyer_name,
buyer_email = order.buyer_email,
buyer_addr = order.delivery_address.as_deref().unwrap_or("N/A"),
link_id = order.link_id,
subtotal = subtotal,
total_tax = total_tax,
shipping = order.delivery_fee,
total = grand_total,
discount_row = if order.discount_amount > 0.0 {
format!(
r#"<div class="total-row" style="color: #00E59B;"><span>Discount</span><span>-₹{:.2}</span></div>"#,
order.discount_amount
)
} else {
String::new()
}
)
}
}