from reportlab.lib.pagesizes import A4 from reportlab.pdfgen import canvas from reportlab.platypus import Table, TableStyle, Paragraph, SimpleDocTemplate, Spacer, Image from reportlab.lib import colors from reportlab.lib.styles import getSampleStyleSheet import io import os import logging # --- helpers --------------------------------------------------------------- def _resolve_logo_path(logo_path: str | None) -> str | None: """Return a readable logo path or None. Tries multiple fallbacks. This avoids crashes when a caller passes a path that exists only in the client container (e.g., '/client/assets/...') while we're running in the server container on Fly. """ here = os.path.dirname(__file__) project_root = os.path.abspath(os.path.join(here, "..", "..")) candidates = [] # 1) if caller provided a path, try it as-is if logo_path: candidates.append(logo_path) # also try without a leading slash inside our image tree stripped = logo_path.lstrip("/\\") candidates.append(os.path.join(project_root, stripped)) # 2) Known locations in this repo layout candidates.append(os.path.join(project_root, "client", "assets", "images", "truck_logo_black.png")) candidates.append(os.path.join(project_root, "assets", "images", "truck_logo_black.png")) # 3) Allow override via env env_path = os.getenv("DEFAULT_LOGO_PATH") if env_path: candidates.insert(0, env_path) for p in candidates: try: if p and os.path.isfile(p): return p except Exception: # some paths may be malformed on certain platforms continue return None def generate_order_pdf(order, user_data, transporter_data, logo_path, save_to_disk=True): #print(order) #print(f'user data: {user_data}') if 'address' in user_data: address = user_data.get("address") if address: address = address.replace(" %",", ") else: address = '' else: address='' buffer = io.BytesIO() doc = SimpleDocTemplate(buffer, pagesize=A4) elements = [] styles = getSampleStyleSheet() # Resolve logo path robustly across local/dev and Fly logo_path = _resolve_logo_path(logo_path) # Prepare texts user_text = f"""{user_data.get("name", "")}
{user_data.get("register_number", "")}
{user_data.get("vat", "")}
{address}
{""}
{user_data.get("contact_name", "")}
{user_data.get("phone", "")}
{user_data.get("email", "")} """ transporter_text = f"""{transporter_data.get("name", "")}
{transporter_data.get("contact_person", "")}
{transporter_data.get("phone", "")}
{transporter_data.get("email", "")} """ # Logo (centered), tolerate missing file logo = None if logo_path: try: logo = Image(logo_path, width=120, mask='auto', height=60) logo.hAlign = 'CENTER' except Exception as e: logging.warning("PDF: failed to load logo at %s: %s", logo_path, e) logo = None # Top section: transporter - logo - user top_section = Table([ [ Paragraph(transporter_text, styles['Normal']), logo, Paragraph(user_text, styles['Normal']) ] ], colWidths=[200, 150, 200]) top_section.setStyle(TableStyle([ ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), ("ALIGN", (0, 0), (0, 0), "LEFT"), ("ALIGN", (1, 0), (1, 0), "CENTER"), ("ALIGN", (2, 0), (2, 0), "RIGHT"), ("LEFTPADDING", (0, 0), (0, 0), 20), ])) elements.append(top_section) elements.append(Spacer(1, 12)) # Order number and current date (centered, vertically stacked) from datetime import datetime header_info = Table([ [Paragraph(f"Loading Order: {order['order_number']}", styles["Normal"])], [Paragraph(f"Date: {datetime.now().strftime('%d/%m/%Y')}", styles["Normal"])] ], colWidths=[450]) header_info.setStyle(TableStyle([ ("ALIGN", (0, 0), (-1, -1), "CENTER") ])) elements.append(header_info) elements.append(Spacer(1, 12)) # Order Summary Table elements.append(Paragraph("Summary", styles['Heading3'])) summary_data = [ ["Details", Paragraph("Values", styles["Normal"])], ["Truck Reg. No.", Paragraph(str(order["track_reg_number"]), styles["Normal"])], ["Trailer Reg. No.", Paragraph(str(order["trailer_reg_number"]), styles["Normal"])], ["Product Description", Paragraph(str(order["products_description"]), styles["Normal"])], ["LDM", Paragraph(str(order["ldb_quantity"]), styles["Normal"])], ["KG", Paragraph(str(order["kg_quantity"]), styles["Normal"])], ["Price", Paragraph(str(order["paid_price"])+ " " +order["currency_paid"], styles["Normal"])], ] summary_table = Table(summary_data, colWidths=[150, 350]) summary_table.setStyle(TableStyle([ ("GRID", (0, 0), (-1, -1), 0.5, colors.black), ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey), ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), ("WORDWRAP", (0, 0), (-1, -1), "CJK"), ("ALIGN", (0, 0), (-1, -1), "LEFT"), ])) elements.append(summary_table) elements.append(Spacer(1, 24)) # Loading Points elements.append(Paragraph("Loading Sequence", styles['Heading3'])) loading_data = [[Paragraph("Address", styles["Normal"]), Paragraph("Date & Hour", styles["Normal"]), Paragraph("Instructions", styles["Normal"])]] for l in order["loading_addresses"]: loading_data.append([ Paragraph(f"{l["loading_address_name"]}: {l["loading_address"]}", styles["Normal"]), Paragraph(f"{l['loading_date']} {l['loading_hour']}", styles["Normal"]), Paragraph(str(l["loading_informatins"]), styles["Normal"]) ]) loading_table = Table(loading_data, colWidths=[200, 100, 200]) loading_table.setStyle(TableStyle([ ("GRID", (0, 0), (-1, -1), 0.5, colors.black), ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey), ("VALIGN", (0, 0), (-1, -1), "TOP"), ("WORDWRAP", (0, 0), (-1, -1), "CJK"), ("ALIGN", (0, 0), (-1, -1), "LEFT"), ])) elements.append(loading_table) elements.append(Spacer(1, 24)) # Unloading Points elements.append(Paragraph("Unloading Sequence", styles['Heading3'])) unloading_data = [[Paragraph("Address", styles["Normal"]), Paragraph("Date & Hour", styles["Normal"]), Paragraph("Instructions", styles["Normal"])]] for u in order["unloading_addresses"]: unloading_data.append([ Paragraph(f"{u["unloading_address_name"]}: {u["unloading_address"]}", styles["Normal"]), Paragraph(f"{u['unloading_date']} {u['unloading_hour']}", styles["Normal"]), Paragraph(str(u["unloading_informatins"]), styles["Normal"]) ]) unloading_table = Table(unloading_data, colWidths=[200, 100, 200]) unloading_table.setStyle(TableStyle([ ("GRID", (0, 0), (-1, -1), 0.5, colors.black), ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey), ("VALIGN", (0, 0), (-1, -1), "TOP"), ("WORDWRAP", (0, 0), (-1, -1), "CJK"), ("ALIGN", (0, 0), (-1, -1), "LEFT"), ])) elements.append(unloading_table) elements.append(Spacer(1, 24)) elements.append(Paragraph("Terms and Conditions", styles["Heading3"]),) elements.append( Paragraph(str(order["terms"]), styles["Normal"]) ) doc.build(elements) buffer.seek(0) user_id = order['user_id'] if user_data['user_role'] == 'company_user': user_id = user_data['company_id'] if save_to_disk: save_path=f"generated_pdfs/order_{user_id}_{order['order_number']}.pdf" with open(save_path, "wb") as f: f.write(buffer.getvalue()) return buffer