Spaces:
Sleeping
Sleeping
| from urllib.parse import quote | |
| from fastapi.staticfiles import StaticFiles | |
| from reportlab.lib.units import cm | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import JSONResponse | |
| from pydantic import BaseModel | |
| from io import BytesIO | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.pdfgen import canvas | |
| from typing import List | |
| import os | |
| app = FastAPI() | |
| def home(): | |
| return {"message": "API para generar facturas PDF"} | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| class ClientInfo(BaseModel): | |
| nombre: str | |
| telefono: str | |
| direccion: str | |
| class ProductItem(BaseModel): | |
| numero_de_item: int | |
| cantidad: int | |
| descripcion: str | |
| precio: float | |
| class InvoiceData(BaseModel): | |
| ruc: str | |
| cliente: ClientInfo | |
| productos: List[ProductItem] | |
| LOGO_PATH = "./img/logo.png" | |
| def generate_pdf(data: InvoiceData): | |
| buffer = BytesIO() | |
| p = canvas.Canvas(buffer, pagesize=A4) | |
| width, height = A4 | |
| # Establecer márgenes | |
| margin_left = 2.54 * cm | |
| margin_top = 2.54 * cm | |
| # Logo de la empresa | |
| logo_width, logo_height = 60, 60 | |
| p.drawInlineImage( | |
| LOGO_PATH, | |
| margin_left, | |
| height - margin_top - logo_height + 25, # Ajuste de 10 puntos para el espacio | |
| width=logo_width, | |
| height=logo_height, | |
| ) | |
| # Nombre de la empresa y número de RUC | |
| p.setFont("Helvetica-Bold", 16) | |
| # Alinear a la derecha | |
| p.drawRightString(width - margin_left, height - margin_top, f"Arte Visual") | |
| p.drawRightString( | |
| width - margin_left, height - margin_top - 20, f"Número de RUC: {data.ruc}" | |
| ) | |
| # Datos del cliente | |
| p.setFont("Helvetica", 12) | |
| p.drawString(margin_left, height - margin_top - 60, "Datos del cliente:") | |
| p.drawString( | |
| margin_left, height - margin_top - 80, f"Nombre: {data.cliente.nombre}" | |
| ) | |
| p.drawString( | |
| margin_left, height - margin_top - 100, f"Teléfono: {data.cliente.telefono}" | |
| ) | |
| p.drawString( | |
| margin_left, | |
| height - margin_top - 120, | |
| f"Dirección: {data.cliente.direccion}", | |
| ) | |
| # Tabla de productos | |
| p.setFont("Helvetica", 12) | |
| p.drawString(margin_left, height - margin_top - 140, "Productos adquiridos:") | |
| headers = [ | |
| "N° de item", | |
| "Cantidad", | |
| "Nombre o descripción del producto", | |
| "Precio", | |
| ] | |
| col_widths = [70, 70, 250, 70] | |
| x_table = margin_left | |
| y_table = height - margin_top - 160 | |
| # Línea horizontal superior de la tabla | |
| p.line(x_table, y_table, x_table + sum(col_widths), y_table) | |
| # Encabezados | |
| p.setFont("Helvetica", 12) | |
| for header, width in zip(headers, col_widths): | |
| p.drawString(x_table, y_table - 15, header) | |
| x_table += width | |
| # Línea horizontal inferior de la tabla | |
| p.line(margin_left, y_table - 20, margin_left + sum(col_widths), y_table - 20) | |
| # Ejemplo de datos de la tabla | |
| y_row = y_table - 40 | |
| for row in data.productos: | |
| x_table = margin_left | |
| for key, width in zip(headers, col_widths): | |
| # Manejo específico para el número de ítem | |
| if key.lower() == "n° de item": | |
| value = row.numero_de_item | |
| elif key.lower() == "nombre o descripción del producto": | |
| value = row.descripcion | |
| elif key.lower() == "precio": | |
| # Alinear el precio a la derecha respetando el margen derecho | |
| value = f"S/. {row.precio:.2f}" | |
| else: | |
| # Utilizamos el nombre de la clave directamente para acceder al valor | |
| value = row.dict().get(key.lower().replace(" ", "_"), None) | |
| p.drawString(x_table, y_row, str(value)) | |
| x_table += width | |
| y_row -= 15 | |
| total = sum(row.cantidad * row.precio for row in data.productos) | |
| p.setFont("Helvetica-Bold", 13) | |
| p.drawString(margin_left + 370, y_row - 30, f"Total: S/. {total}") | |
| p.showPage() | |
| p.save() | |
| buffer.seek(0) | |
| return buffer | |
| from fastapi import Request | |
| app.mount("/pdf", StaticFiles(directory="pdfs"), name="pdf") | |
| def generate_and_redirect(data: InvoiceData): | |
| try: | |
| buffer = generate_pdf(data) | |
| directorio_actual = os.getcwd() | |
| pdfs_directory = os.path.join(directorio_actual, "pdfs") | |
| pdfs_directory = os.path.normpath(pdfs_directory) | |
| if not os.path.exists(pdfs_directory): | |
| os.makedirs(pdfs_directory) | |
| file_name = f"factura_{data.cliente.nombre.replace(' ', '_')}.pdf" | |
| file_path = os.path.join(pdfs_directory, file_name) | |
| file_path = os.path.normpath(file_path) | |
| with open(file_path, "wb") as file: | |
| file.write(buffer.read()) | |
| pdf_url = f"/pdf/{quote(file_name)}" | |
| return JSONResponse(content={"pdf_url": pdf_url}) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |