Spaces:
Running
Running
| 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() | |
| } | |
| ) | |
| } | |
| } | |