File size: 5,086 Bytes
06519d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4295581
 
 
 
 
 
06519d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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()


@app.get("/")
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")


@app.post("/generate_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))