from fpdf import FPDF import os from datetime import datetime REPORT_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static", "reports") if not os.path.exists(REPORT_DIR): os.makedirs(REPORT_DIR) class PDFReport(FPDF): def __init__(self, title="BMS AI Assistant Report"): super().__init__() self.report_title = title def header(self): self.set_font('Arial', 'B', 15) self.cell(0, 10, self.report_title, 0, 1, 'C') self.ln(10) def footer(self): self.set_y(-15) self.set_font('Arial', 'I', 8) self.cell(0, 10, 'Page ' + str(self.page_no()), 0, 0, 'C') def generate_forecast_pdf(item_code, forecast_data, location=None): pdf = PDFReport("Demand Forecasting Report") pdf.add_page() # Title Info pdf.set_font('Arial', 'B', 12) pdf.cell(0, 10, f'Item: {item_code}', 0, 1) pdf.cell(0, 10, f'Location: {location if location else "All Locations"}', 0, 1) pdf.cell(0, 10, f'Date Generated: {datetime.now().strftime("%Y-%m-%d %H:%M")}', 0, 1) pdf.ln(10) # Table Header pdf.set_font('Arial', 'B', 10) pdf.cell(60, 10, 'Date', 1) pdf.cell(40, 10, 'Quantity', 1) pdf.ln() # Table Data pdf.set_font('Arial', '', 10) total_qty = 0 for row in forecast_data: pdf.cell(60, 10, str(row['date']), 1) pdf.cell(40, 10, str(row['qty']), 1) pdf.ln() total_qty += row['qty'] pdf.ln(5) pdf.set_font('Arial', 'B', 10) pdf.cell(60, 10, 'Total Forecasted Demand:', 0) pdf.cell(40, 10, str(total_qty), 0) filename = f"forecast_{item_code}_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf" filepath = os.path.join(REPORT_DIR, filename) pdf.output(filepath) return filename def generate_inventory_pdf(item_code, inventory_data, location=None): pdf = PDFReport("Inventory Report") pdf.add_page() # Title Info pdf.set_font('Arial', 'B', 12) pdf.cell(0, 10, f'Item: {item_code}', 0, 1) pdf.cell(0, 10, f'Location: {location if location else "All Locations"}', 0, 1) pdf.cell(0, 10, f'Date Generated: {datetime.now().strftime("%Y-%m-%d %H:%M")}', 0, 1) pdf.ln(10) # Table Header pdf.set_font('Arial', 'B', 10) pdf.cell(30, 10, 'Location', 1) pdf.cell(20, 10, 'Qty', 1) pdf.cell(30, 10, 'Status', 1) pdf.cell(30, 10, 'Bin', 1) pdf.cell(30, 10, 'Lot', 1) pdf.cell(30, 10, 'Expiry', 1) pdf.ln() # Table Data pdf.set_font('Arial', '', 10) total_on_hand = 0 for row in inventory_data: loc = row.get('region', 'Unknown') qty = row.get('qty_on_hand', 0) status = row.get('status', 'Unknown') bin_loc = row.get('bin_location', '-') or '-' lot = row.get('lot_number', '-') or '-' expiry = row.get('expiry_date', '-') or '-' pdf.cell(30, 10, str(loc)[:15], 1) pdf.cell(20, 10, str(qty), 1) pdf.cell(30, 10, str(status), 1) pdf.cell(30, 10, str(bin_loc), 1) pdf.cell(30, 10, str(lot), 1) pdf.cell(30, 10, str(expiry), 1) pdf.ln() try: total_on_hand += int(qty) except: pass pdf.ln(5) pdf.set_font('Arial', 'B', 10) pdf.cell(60, 10, 'Total On Hand:', 0) pdf.cell(40, 10, str(total_on_hand), 0) filename = f"inventory_{item_code}_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf" filepath = os.path.join(REPORT_DIR, filename) pdf.output(filepath) return filename def generate_general_pdf(title, content): pdf = PDFReport(title) pdf.add_page() pdf.set_font('Arial', 'B', 12) pdf.cell(0, 10, title, 0, 1) pdf.ln(5) pdf.set_font('Arial', '', 10) pdf.multi_cell(0, 10, content) filename = f"report_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf" filepath = os.path.join(REPORT_DIR, filename) pdf.output(filepath) return filename def generate_competitor_pdf(item_code, analysis_data, insights): pdf = PDFReport("Competitor Analysis Report") pdf.add_page() # Title Info pdf.set_font('Arial', 'B', 12) pdf.cell(0, 10, f'Item: {item_code}', 0, 1) pdf.cell(0, 10, f'Date Generated: {datetime.now().strftime("%Y-%m-%d %H:%M")}', 0, 1) pdf.ln(5) # Insights Section pdf.set_fill_color(230, 255, 250) # Light teal background pdf.set_font('Arial', 'B', 11) pdf.cell(0, 10, "Expert Market Insights:", 0, 1) pdf.set_font('Arial', 'I', 10) # Clean up markdown bolding for PDF clean_insights = insights.replace("**", "").replace("
", "\n") pdf.multi_cell(0, 8, clean_insights, 0, 'L', True) pdf.ln(10) # Table Header pdf.set_font('Arial', 'B', 10) pdf.cell(20, 10, 'Year', 1) pdf.cell(30, 10, 'Comp Price', 1) pdf.cell(30, 10, 'Our Price', 1) pdf.cell(30, 10, 'Sales Qty', 1) pdf.cell(25, 10, 'Share %', 1) pdf.cell(25, 10, 'Rating', 1) pdf.ln() # Table Data pdf.set_font('Arial', '', 10) for row in analysis_data: share = row.get('market_share_percentage', 'N/A') or 'N/A' rating = row.get('customer_rating', 'N/A') or 'N/A' try: share_val = float(share) share_str = f"{share_val:.2f}%" except: share_str = f"{share}%" try: rating_val = float(rating) rating_str = f"{rating_val:.2f}" except: rating_str = str(rating) pdf.cell(20, 10, str(row['year']), 1) pdf.cell(30, 10, f"${row['competitor_price']:.2f}", 1) pdf.cell(30, 10, f"${row['our_price']:.2f}", 1) pdf.cell(30, 10, str(row['sales_qty']), 1) pdf.cell(25, 10, share_str, 1) pdf.cell(25, 10, rating_str, 1) pdf.ln() filename = f"competitor_{item_code}_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf" filepath = os.path.join(REPORT_DIR, filename) pdf.output(filepath) return filename def generate_lost_sales_pdf(item_code, loss_data, location=None): pdf = PDFReport("Lost Sales Analysis Report") pdf.add_page() # Title Info pdf.set_font('Arial', 'B', 12) pdf.cell(0, 10, f'Item: {item_code if item_code else "All Items"}', 0, 1) if location: pdf.cell(0, 10, f'Location: {location}', 0, 1) pdf.cell(0, 10, f'Date Generated: {datetime.now().strftime("%Y-%m-%d %H:%M")}', 0, 1) pdf.ln(10) # Table Header pdf.set_font('Arial', 'B', 10) pdf.cell(60, 10, 'Primary Reason', 1) pdf.cell(40, 10, 'Lost Qty', 1) pdf.cell(50, 10, 'Est. Revenue Lost', 1) pdf.ln() # Table Data pdf.set_font('Arial', '', 10) total_rev = 0 # Sort by lost qty desc sorted_data = sorted(loss_data, key=lambda x: x['lost_qty'], reverse=True) for row in sorted_data: pdf.cell(60, 10, str(row['reason']), 1) pdf.cell(40, 10, str(row['lost_qty']), 1) pdf.cell(50, 10, f"${row['estimated_revenue_lost']:,.2f}", 1) pdf.ln() total_rev += row['estimated_revenue_lost'] pdf.ln(5) pdf.set_font('Arial', 'B', 10) pdf.cell(100, 10, 'Total Estimated Loss:', 0) pdf.cell(50, 10, f"${total_rev:,.2f}", 0) filename = f"lost_sales_{item_code if item_code else 'summary'}_{datetime.now().strftime('%Y%m%d%H%M%S')}.pdf" filepath = os.path.join(REPORT_DIR, filename) pdf.output(filepath) return filename