JairoDanielMT commited on
Commit
06519d7
·
1 Parent(s): 86f0d9b
Files changed (5) hide show
  1. .gitignore +0 -0
  2. Dockerfile +15 -0
  3. app.py +172 -0
  4. img/logo.png +0 -0
  5. requirements.txt +3 -0
.gitignore ADDED
File without changes
Dockerfile ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Usa una imagen base de Python
2
+ FROM python:3.9
3
+ # Establece el directorio de trabajo
4
+ WORKDIR /code
5
+
6
+ # Copia los archivos necesarios al contenedor
7
+ COPY ./requirements.txt /code/requirements.txt
8
+ RUN pip install --no-cache-dir -r /code/requirements.txt
9
+
10
+ COPY . .
11
+
12
+ RUN chmod -R 777 /code
13
+
14
+ # Comando para ejecutar la aplicación
15
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from urllib.parse import quote
2
+ from fastapi.staticfiles import StaticFiles
3
+ from reportlab.lib.units import cm
4
+ from fastapi import FastAPI, HTTPException
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from fastapi.responses import JSONResponse
7
+ from pydantic import BaseModel
8
+ from io import BytesIO
9
+ from reportlab.lib.pagesizes import A4
10
+ from reportlab.pdfgen import canvas
11
+ from typing import List
12
+ import os
13
+
14
+ app = FastAPI()
15
+
16
+ app.add_middleware(
17
+ CORSMiddleware,
18
+ allow_origins=["*"],
19
+ allow_credentials=True,
20
+ allow_methods=["*"],
21
+ allow_headers=["*"],
22
+ )
23
+
24
+
25
+ class ClientInfo(BaseModel):
26
+ nombre: str
27
+ telefono: str
28
+ direccion: str
29
+
30
+
31
+ class ProductItem(BaseModel):
32
+ numero_de_item: int
33
+ cantidad: int
34
+ descripcion: str
35
+ precio: float
36
+
37
+
38
+ class InvoiceData(BaseModel):
39
+ ruc: str
40
+ cliente: ClientInfo
41
+ productos: List[ProductItem]
42
+
43
+
44
+ LOGO_PATH = "./img/logo.png"
45
+
46
+
47
+ def generate_pdf(data: InvoiceData):
48
+ buffer = BytesIO()
49
+ p = canvas.Canvas(buffer, pagesize=A4)
50
+ width, height = A4
51
+
52
+ # Establecer márgenes
53
+ margin_left = 2.54 * cm
54
+ margin_top = 2.54 * cm
55
+
56
+ # Logo de la empresa
57
+ logo_width, logo_height = 60, 60
58
+ p.drawInlineImage(
59
+ LOGO_PATH,
60
+ margin_left,
61
+ height - margin_top - logo_height + 25, # Ajuste de 10 puntos para el espacio
62
+ width=logo_width,
63
+ height=logo_height,
64
+ )
65
+
66
+ # Nombre de la empresa y número de RUC
67
+ p.setFont("Helvetica-Bold", 16)
68
+ # Alinear a la derecha
69
+ p.drawRightString(width - margin_left, height - margin_top, f"Arte Visual")
70
+ p.drawRightString(
71
+ width - margin_left, height - margin_top - 20, f"Número de RUC: {data.ruc}"
72
+ )
73
+
74
+ # Datos del cliente
75
+ p.setFont("Helvetica", 12)
76
+ p.drawString(margin_left, height - margin_top - 60, "Datos del cliente:")
77
+ p.drawString(
78
+ margin_left, height - margin_top - 80, f"Nombre: {data.cliente.nombre}"
79
+ )
80
+ p.drawString(
81
+ margin_left, height - margin_top - 100, f"Teléfono: {data.cliente.telefono}"
82
+ )
83
+ p.drawString(
84
+ margin_left,
85
+ height - margin_top - 120,
86
+ f"Dirección: {data.cliente.direccion}",
87
+ )
88
+
89
+ # Tabla de productos
90
+ p.setFont("Helvetica", 12)
91
+ p.drawString(margin_left, height - margin_top - 140, "Productos adquiridos:")
92
+ headers = [
93
+ "N° de item",
94
+ "Cantidad",
95
+ "Nombre o descripción del producto",
96
+ "Precio",
97
+ ]
98
+ col_widths = [70, 70, 250, 70]
99
+ x_table = margin_left
100
+ y_table = height - margin_top - 160
101
+
102
+ # Línea horizontal superior de la tabla
103
+ p.line(x_table, y_table, x_table + sum(col_widths), y_table)
104
+
105
+ # Encabezados
106
+ p.setFont("Helvetica", 12)
107
+ for header, width in zip(headers, col_widths):
108
+ p.drawString(x_table, y_table - 15, header)
109
+ x_table += width
110
+
111
+ # Línea horizontal inferior de la tabla
112
+ p.line(margin_left, y_table - 20, margin_left + sum(col_widths), y_table - 20)
113
+
114
+ # Ejemplo de datos de la tabla
115
+ y_row = y_table - 40
116
+ for row in data.productos:
117
+ x_table = margin_left
118
+ for key, width in zip(headers, col_widths):
119
+ # Manejo específico para el número de ítem
120
+ if key.lower() == "n° de item":
121
+ value = row.numero_de_item
122
+ elif key.lower() == "nombre o descripción del producto":
123
+ value = row.descripcion
124
+ elif key.lower() == "precio":
125
+ # Alinear el precio a la derecha respetando el margen derecho
126
+ value = f"S/. {row.precio:.2f}"
127
+ else:
128
+ # Utilizamos el nombre de la clave directamente para acceder al valor
129
+ value = row.dict().get(key.lower().replace(" ", "_"), None)
130
+ p.drawString(x_table, y_row, str(value))
131
+ x_table += width
132
+ y_row -= 15
133
+
134
+ total = sum(row.cantidad * row.precio for row in data.productos)
135
+ p.setFont("Helvetica-Bold", 13)
136
+ p.drawString(margin_left + 370, y_row - 30, f"Total: S/. {total}")
137
+
138
+ p.showPage()
139
+ p.save()
140
+ buffer.seek(0)
141
+
142
+ return buffer
143
+
144
+
145
+ from fastapi import Request
146
+
147
+ app.mount("/pdf", StaticFiles(directory="pdfs"), name="pdf")
148
+
149
+
150
+ @app.post("/generate_pdf")
151
+ def generate_and_redirect(data: InvoiceData):
152
+ try:
153
+ buffer = generate_pdf(data)
154
+
155
+ directorio_actual = os.getcwd()
156
+ pdfs_directory = os.path.join(directorio_actual, "pdfs")
157
+ pdfs_directory = os.path.normpath(pdfs_directory)
158
+
159
+ if not os.path.exists(pdfs_directory):
160
+ os.makedirs(pdfs_directory)
161
+
162
+ file_name = f"factura_{data.cliente.nombre.replace(' ', '_')}.pdf"
163
+ file_path = os.path.join(pdfs_directory, file_name)
164
+ file_path = os.path.normpath(file_path)
165
+
166
+ with open(file_path, "wb") as file:
167
+ file.write(buffer.read())
168
+ pdf_url = f"/pdf/{quote(file_name)}"
169
+ return JSONResponse(content={"pdf_url": pdf_url})
170
+
171
+ except Exception as e:
172
+ raise HTTPException(status_code=500, detail=str(e))
img/logo.png ADDED
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ reportlab