EL GHAFRAOUI AYOUB commited on
Commit
594b207
·
1 Parent(s): 85031c9
app/services/__pycache__/invoice_service.cpython-312.pyc CHANGED
Binary files a/app/services/__pycache__/invoice_service.cpython-312.pyc and b/app/services/__pycache__/invoice_service.cpython-312.pyc differ
 
app/services/invoice_service copy.py ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from reportlab.lib.pagesizes import A4
2
+ from reportlab.pdfgen import canvas
3
+ from reportlab.lib import colors
4
+ from reportlab.lib.units import cm
5
+ from io import BytesIO
6
+ from app.db.models import Invoice
7
+ import logging
8
+ import os
9
+ import datetime
10
+
11
+ # Set up logging
12
+ logger = logging.getLogger(__name__)
13
+
14
+ class InvoiceService:
15
+ @staticmethod
16
+ def generate_pdf(data: Invoice) -> bytes:
17
+ try:
18
+ buffer = BytesIO()
19
+ pdf = canvas.Canvas(buffer, pagesize=A4)
20
+ page_width, page_height = A4
21
+
22
+ # Constants
23
+ HEADER_BLUE = (0.29, 0.45, 0.68)
24
+ BLUE_LIGHT = (1.5, 1.5, 1)
25
+ WHITE = (1, 1, 1)
26
+ BLACK = (0, 0, 0)
27
+ MARGIN = 30
28
+ LINE_HEIGHT = 20
29
+ BOX_PADDING = 10
30
+ STANDARD_FONT_SIZE = 10 # Add standard font size constant
31
+
32
+ # Helper function to draw centered text
33
+ def draw_centered_text(pdf, text, x, y, width, font="Helvetica", size=STANDARD_FONT_SIZE):
34
+ text_width = pdf.stringWidth(text, font, size)
35
+ pdf.drawString(x + (width - text_width) / 2, y, text)
36
+
37
+ # Helper function to draw a bordered box
38
+ def draw_box(pdf, x, y, width, height, fill_color=None, stroke_color=BLACK):
39
+ if fill_color:
40
+ pdf.setFillColorRGB(*fill_color)
41
+ pdf.rect(x, y, width, height, fill=1, stroke=0)
42
+ pdf.setFillColorRGB(*stroke_color)
43
+ pdf.rect(x, y, width, height, stroke=1)
44
+
45
+ # Get the absolute path to the logo file
46
+ current_dir = os.path.dirname(os.path.abspath(__file__))
47
+ logo_path = os.path.join(current_dir, "..", "static", "logo.png")
48
+
49
+ # Top section layout
50
+ top_margin = page_height - 100
51
+
52
+ # Left side: Logo - moved far left and up
53
+ if os.path.exists(logo_path):
54
+ pdf.drawImage(logo_path, MARGIN, top_margin + 30, width=100, height=60)
55
+
56
+ # Right side: DEVIS and Client Box - moved far right and up
57
+ pdf.setFont("Helvetica-Bold", 36) # Reduced from 48 to be more consistent
58
+ devis_text = "DEVIS"
59
+ devis_width = pdf.stringWidth(devis_text, "Helvetica-Bold", 36)
60
+ devis_x = page_width - devis_width - MARGIN - 10
61
+ devis_y = top_margin + 50
62
+ pdf.drawString(devis_x, devis_y, devis_text)
63
+
64
+ # Client info box - moved right under DEVIS
65
+ box_width = 200
66
+ box_height = 80
67
+ box_x = page_width - box_width - MARGIN + 10
68
+ box_y = devis_y - 120
69
+
70
+ # Draw client box
71
+ pdf.rect(box_x, box_y, box_width, box_height, stroke=1)
72
+
73
+ # Client Info
74
+ pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
75
+ client_info = [
76
+ data.client_name,
77
+ data.project,
78
+ data.address,
79
+ data.client_phone
80
+ ]
81
+
82
+ # Center and draw each line of client info
83
+ line_height = box_height / (len(client_info) + 1)
84
+ for i, text in enumerate(client_info):
85
+ text_width = pdf.stringWidth(str(text), "Helvetica", STANDARD_FONT_SIZE)
86
+ x = box_x + (box_width - text_width) / 2
87
+ y = box_y + box_height - ((i + 1) * line_height)
88
+ pdf.drawString(x, y, str(text))
89
+
90
+ # Info boxes (Date, N° Devis, PLANCHER) - adjusted starting position
91
+ info_y = top_margin - 30
92
+ box_label_width = 120
93
+ box_value_width = 80
94
+
95
+ for label, value in [
96
+ ("Date du devis :", data.date.strftime("%d/%m/%Y")),
97
+ ("N° Devis :", data.invoice_number),
98
+ ("PLANCHER :", data.frame_number or "PH RDC")
99
+ ]:
100
+ draw_box(pdf, MARGIN, info_y, box_label_width, LINE_HEIGHT, fill_color=HEADER_BLUE)
101
+ pdf.setFillColorRGB(*WHITE)
102
+ pdf.drawString(MARGIN + BOX_PADDING, info_y + 6, label)
103
+
104
+ draw_box(pdf, MARGIN + box_label_width, info_y, box_value_width, LINE_HEIGHT, fill_color=WHITE)
105
+ pdf.setFillColorRGB(*BLACK)
106
+ draw_centered_text(pdf, str(value), MARGIN + box_label_width, info_y + 6, box_value_width)
107
+
108
+ info_y -= 25
109
+
110
+ # Table headers
111
+ table_y = info_y - 30
112
+ headers = [
113
+ ("Description", 150),
114
+ ("Unité", 50),
115
+ ("NBRE", 50),
116
+ ("LNG/Qté", 60),
117
+ ("P.U", 60),
118
+ ("Total HT", 170)
119
+ ]
120
+
121
+ total_width = sum(width for _, width in headers)
122
+ table_x = (page_width - total_width) / 2 # Center table
123
+ draw_box(pdf, table_x, table_y, total_width, LINE_HEIGHT, fill_color=HEADER_BLUE)
124
+ pdf.setFillColorRGB(*WHITE)
125
+ # add little bit of space
126
+
127
+
128
+ current_x = table_x
129
+ for title, width in headers:
130
+ draw_box(pdf, current_x, table_y, width, LINE_HEIGHT)
131
+ pdf.setFillColorRGB(*WHITE)
132
+ draw_centered_text(pdf, title, current_x, table_y + 6, width)
133
+ current_x += width
134
+
135
+
136
+ # Draw sections and items
137
+ current_y = table_y - LINE_HEIGHT - 10
138
+
139
+
140
+
141
+ def draw_section_header2(title):
142
+ nonlocal current_y
143
+ draw_box(pdf, table_x, current_y, total_width, LINE_HEIGHT, fill_color=WHITE)
144
+
145
+ # Set the font to a bold variant
146
+ pdf.setFont("Helvetica-Bold", 9) # Adjust the font name and size as needed
147
+
148
+ # Set the fill color to black
149
+ pdf.setFillColorRGB(*BLACK) # RGB values for black
150
+
151
+ # Draw the string
152
+ pdf.drawString(table_x + BOX_PADDING, current_y + 6, title)
153
+
154
+ current_y -= LINE_HEIGHT
155
+ pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
156
+
157
+ def format_currency(value):
158
+ # Format with 2 decimal places and thousands separator
159
+ return "{:,.2f}".format(value).replace(",", " ")
160
+
161
+
162
+ def draw_item_row(item, indent=False):
163
+ nonlocal current_y
164
+ pdf.setFillColorRGB(*BLACK)
165
+ current_x = table_x
166
+
167
+ draw_box(pdf, current_x, current_y, total_width, LINE_HEIGHT, fill_color=WHITE)
168
+
169
+ cells = [
170
+ (" " + item.description if indent else item.description, 150),
171
+ (item.unit, 50),
172
+ (str(item.quantity), 50),
173
+ (f"{item.length:.2f}", 60),
174
+ (f"{format_currency(item.unit_price)}", 60),
175
+ (f"{format_currency(item.total_price)} DH", 170) # Total column
176
+ ]
177
+
178
+ for i, (value, width) in enumerate(cells):
179
+ draw_box(pdf, current_x, current_y, width, LINE_HEIGHT)
180
+
181
+ if i == len(cells) - 1: # If it's the last column (Total DH)
182
+ pdf.setFont("Helvetica-Bold", STANDARD_FONT_SIZE) # Make it bold
183
+ else:
184
+ pdf.setFont("Helvetica", STANDARD_FONT_SIZE) # Normal font
185
+
186
+ if isinstance(value, str) and value.startswith(" "):
187
+ pdf.drawString(current_x + 20, current_y + 6, value.strip())
188
+ else:
189
+ draw_centered_text(pdf, str(value), current_x, current_y + 6, width)
190
+
191
+ current_x += width
192
+
193
+ pdf.setFont("Helvetica", STANDARD_FONT_SIZE) # Reset font to normal for next rows
194
+ current_y -= LINE_HEIGHT
195
+
196
+
197
+ # Draw sections
198
+ sections = [
199
+ ("POUTRELLES :", "PCP"),
200
+ ("HOURDIS :", "HOURDIS"),
201
+ ("PANNEAU TREILLIS SOUDES :", "PTS"),
202
+ ("AGGLOS :", "AGGLOS")
203
+ ]
204
+
205
+ for section_title, keyword in sections:
206
+ print("-------------")
207
+ print(f"section_title: {section_title}")
208
+ draw_section_header2(section_title)
209
+ items = [i for i in data.items if keyword in i.description]
210
+ for item in items:
211
+ draw_item_row(item, indent=(keyword != "lfflflflf"))
212
+
213
+ # NB box with text
214
+ nb_box_width = 200
215
+ nb_box_height = 80
216
+ pdf.setFillColorRGB(*BLACK)
217
+ pdf.rect(20, current_y - nb_box_height, nb_box_width, nb_box_height, stroke=1)
218
+ pdf.setFont("Helvetica-Bold", STANDARD_FONT_SIZE)
219
+ pdf.drawString(30, current_y - nb_box_height + 60, "NB:")
220
+
221
+ # Add the new text
222
+ pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
223
+ nb_text = "Toute modification apportée aux plans BA initialement fournis, entraine automatiquement la modification de ce devis."
224
+ # Split text to fit in box
225
+ words = nb_text.split()
226
+ lines = []
227
+ current_line = []
228
+
229
+ for word in words:
230
+ current_line.append(word)
231
+ # Check if current line width exceeds box width
232
+ if pdf.stringWidth(' '.join(current_line), "Helvetica", STANDARD_FONT_SIZE) > nb_box_width - 20:
233
+ current_line.pop() # Remove last word
234
+ lines.append(' '.join(current_line))
235
+ current_line = [word]
236
+
237
+ if current_line:
238
+ lines.append(' '.join(current_line))
239
+
240
+ # Draw each line
241
+ for i, line in enumerate(lines):
242
+ pdf.drawString(30, current_y - nb_box_height + 45 - (i * 10), line)
243
+
244
+ # ADD text after the NB box
245
+ pdf.setFont("Helvetica-Bold", 9)
246
+ pdf.drawString(30 , current_y - nb_box_height - 15, "Validité du devis : 1 mois")
247
+
248
+
249
+ # Totals section
250
+ # set font to bold
251
+ pdf.setFont("Helvetica-Bold", 12)
252
+ current_y -= 20
253
+ totals_table_width = 300
254
+ row_height = 20
255
+
256
+ for i, (label1, label2, value) in enumerate([
257
+ ("Total", "H.T", f"{format_currency(data.total_ht)} DH"),
258
+ ("TVA", "20 %", f"{format_currency(data.tax)} DH"),
259
+ ("Total", "TTC", f"{format_currency(data.total_ttc)} DH")
260
+ ]):
261
+ y = current_y - (i * row_height)
262
+ totals_x = (page_width - totals_table_width) - 27
263
+ draw_box(pdf, totals_x, y, totals_table_width / 2, row_height)
264
+ draw_box(pdf, totals_x + totals_table_width / 2, y, totals_table_width / 2, row_height)
265
+ pdf.drawString(totals_x + 10, y + 6, f"{label1} {label2}")
266
+ pdf.drawRightString(totals_x + totals_table_width - 10, y + 6, value)
267
+
268
+ # Footer
269
+ pdf.setFont("Helvetica", STANDARD_FONT_SIZE - 3)
270
+ footer_text = "Douar Ait Laarassi Tidili, Cercle El Kelâa, Route de Safi, Km 14-40000 Marrakech"
271
+ pdf.drawCentredString(page_width / 2 + 20, 30, footer_text)
272
+
273
+ # add the commercial phone number
274
+ # i have salah with 0666666666 and khaled with 077777777 and ismale with 08888888 and jamal with 099999999
275
+ commercial_info = dict(
276
+ salah = "06 62 29 99 78",
277
+ khaled= "06 66 24 80 94",
278
+ ismail= "06 66 24 50 15",
279
+ jamal = "06 70 08 36 50"
280
+ )
281
+ # Add commercial info to footer
282
+ print(f"Commercial value: {data.commercial}") # Add this debug line
283
+ if data.commercial and data.commercial.lower() != 'divers':
284
+ commercial_text = f"Commercial: {data.commercial.upper()}"
285
+ commercial_phone = f"Tél: {commercial_info.get(data.commercial.lower(), '')}"
286
+
287
+ pdf.drawString(MARGIN + 10, 30, commercial_text)
288
+ #draw under the commercial text :
289
+ pdf.drawString(MARGIN + 10, 20, commercial_phone)
290
+ footer_contact = "Tél: 05 24 01 55 54 Fax : 05 24 01 55 29 E-mail : [email protected]"
291
+ pdf.drawCentredString(page_width / 2 + 10, 20, footer_contact)
292
+ ## add the time and page number
293
+
294
+ pdf.drawString(page_width - 100, 30, f"Date: {datetime.datetime.now().strftime('%d/%m/%Y')}")
295
+ pdf.drawString(page_width - 100, 20, f"Page {pdf.getPageNumber()}/{pdf.getPageNumber()}")
296
+ else:
297
+ footer_contact = "Tél: 05 24 01 55 54 Fax : 05 24 01 55 29 E-mail : [email protected]"
298
+ pdf.drawCentredString(page_width / 2 + 10, 20, footer_contact)
299
+ pdf.drawString(page_width - 100, 30, f"Date: {datetime.datetime().now.strftime('%d/%m/%Y')}")
300
+ pdf.drawString(page_width - 100, 20, f"Page {pdf.getPageNumber()}/{pdf.getPageNumber()}")
301
+
302
+ pdf.save()
303
+ buffer.seek(0)
304
+ return buffer.getvalue()
305
+
306
+ except Exception as e:
307
+ logger.error(f"Error in PDF generation: {str(e)}", exc_info=True)
308
+ raise
app/services/invoice_service.py CHANGED
@@ -27,7 +27,8 @@ class InvoiceService:
27
  MARGIN = 30
28
  LINE_HEIGHT = 20
29
  BOX_PADDING = 10
30
- STANDARD_FONT_SIZE = 10 # Add standard font size constant
 
31
 
32
  # Helper function to draw centered text
33
  def draw_centered_text(pdf, text, x, y, width, font="Helvetica", size=STANDARD_FONT_SIZE):
@@ -54,7 +55,7 @@ class InvoiceService:
54
  pdf.drawImage(logo_path, MARGIN, top_margin + 30, width=100, height=60)
55
 
56
  # Right side: DEVIS and Client Box - moved far right and up
57
- pdf.setFont("Helvetica-Bold", 36) # Reduced from 48 to be more consistent
58
  devis_text = "DEVIS"
59
  devis_width = pdf.stringWidth(devis_text, "Helvetica-Bold", 36)
60
  devis_x = page_width - devis_width - MARGIN - 10
@@ -122,43 +123,29 @@ class InvoiceService:
122
  table_x = (page_width - total_width) / 2 # Center table
123
  draw_box(pdf, table_x, table_y, total_width, LINE_HEIGHT, fill_color=HEADER_BLUE)
124
  pdf.setFillColorRGB(*WHITE)
125
- # add little bit of space
126
-
127
-
128
- current_x = table_x
129
  for title, width in headers:
130
  draw_box(pdf, current_x, table_y, width, LINE_HEIGHT)
131
  pdf.setFillColorRGB(*WHITE)
132
  draw_centered_text(pdf, title, current_x, table_y + 6, width)
133
  current_x += width
134
 
135
-
136
  # Draw sections and items
137
  current_y = table_y - LINE_HEIGHT - 10
138
 
139
-
140
-
141
  def draw_section_header2(title):
142
  nonlocal current_y
143
  draw_box(pdf, table_x, current_y, total_width, LINE_HEIGHT, fill_color=WHITE)
144
-
145
- # Set the font to a bold variant
146
- pdf.setFont("Helvetica-Bold", 9) # Adjust the font name and size as needed
147
-
148
- # Set the fill color to black
149
- pdf.setFillColorRGB(*BLACK) # RGB values for black
150
-
151
- # Draw the string
152
  pdf.drawString(table_x + BOX_PADDING, current_y + 6, title)
153
-
154
  current_y -= LINE_HEIGHT
155
  pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
156
 
157
  def format_currency(value):
158
- # Format with 2 decimal places and thousands separator
159
  return "{:,.2f}".format(value).replace(",", " ")
160
 
161
-
162
  def draw_item_row(item, indent=False):
163
  nonlocal current_y
164
  pdf.setFillColorRGB(*BLACK)
@@ -172,16 +159,16 @@ class InvoiceService:
172
  (str(item.quantity), 50),
173
  (f"{item.length:.2f}", 60),
174
  (f"{format_currency(item.unit_price)}", 60),
175
- (f"{format_currency(item.total_price)} DH", 170) # Total column
176
  ]
177
 
178
  for i, (value, width) in enumerate(cells):
179
  draw_box(pdf, current_x, current_y, width, LINE_HEIGHT)
180
 
181
- if i == len(cells) - 1: # If it's the last column (Total DH)
182
- pdf.setFont("Helvetica-Bold", STANDARD_FONT_SIZE) # Make it bold
183
  else:
184
- pdf.setFont("Helvetica", STANDARD_FONT_SIZE) # Normal font
185
 
186
  if isinstance(value, str) and value.startswith(" "):
187
  pdf.drawString(current_x + 20, current_y + 6, value.strip())
@@ -190,9 +177,23 @@ class InvoiceService:
190
 
191
  current_x += width
192
 
193
- pdf.setFont("Helvetica", STANDARD_FONT_SIZE) # Reset font to normal for next rows
194
  current_y -= LINE_HEIGHT
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
  # Draw sections
198
  sections = [
@@ -203,8 +204,6 @@ class InvoiceService:
203
  ]
204
 
205
  for section_title, keyword in sections:
206
- print("-------------")
207
- print(f"section_title: {section_title}")
208
  draw_section_header2(section_title)
209
  items = [i for i in data.items if keyword in i.description]
210
  for item in items:
@@ -217,37 +216,32 @@ class InvoiceService:
217
  pdf.rect(20, current_y - nb_box_height, nb_box_width, nb_box_height, stroke=1)
218
  pdf.setFont("Helvetica-Bold", STANDARD_FONT_SIZE)
219
  pdf.drawString(30, current_y - nb_box_height + 60, "NB:")
220
-
221
  # Add the new text
222
  pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
223
  nb_text = "Toute modification apportée aux plans BA initialement fournis, entraine automatiquement la modification de ce devis."
224
- # Split text to fit in box
225
  words = nb_text.split()
226
  lines = []
227
  current_line = []
228
-
229
  for word in words:
230
  current_line.append(word)
231
- # Check if current line width exceeds box width
232
  if pdf.stringWidth(' '.join(current_line), "Helvetica", STANDARD_FONT_SIZE) > nb_box_width - 20:
233
- current_line.pop() # Remove last word
234
  lines.append(' '.join(current_line))
235
  current_line = [word]
236
-
237
  if current_line:
238
  lines.append(' '.join(current_line))
239
-
240
- # Draw each line
241
  for i, line in enumerate(lines):
242
  pdf.drawString(30, current_y - nb_box_height + 45 - (i * 10), line)
243
 
244
  # ADD text after the NB box
245
  pdf.setFont("Helvetica-Bold", 9)
246
- pdf.drawString(30 , current_y - nb_box_height - 15, "Validité du devis : 1 mois")
247
-
248
-
249
  # Totals section
250
- # set font to bold
251
  pdf.setFont("Helvetica-Bold", 12)
252
  current_y -= 20
253
  totals_table_width = 300
@@ -270,33 +264,27 @@ class InvoiceService:
270
  footer_text = "Douar Ait Laarassi Tidili, Cercle El Kelâa, Route de Safi, Km 14-40000 Marrakech"
271
  pdf.drawCentredString(page_width / 2 + 20, 30, footer_text)
272
 
273
- # add the commercial phone number
274
- # i have salah with 0666666666 and khaled with 077777777 and ismale with 08888888 and jamal with 099999999
275
  commercial_info = dict(
276
- salah = "06 62 29 99 78",
277
- khaled= "06 66 24 80 94",
278
- ismail= "06 66 24 50 15",
279
- jamal = "06 70 08 36 50"
280
  )
281
- # Add commercial info to footer
282
- print(f"Commercial value: {data.commercial}") # Add this debug line
283
  if data.commercial and data.commercial.lower() != 'divers':
284
  commercial_text = f"Commercial: {data.commercial.upper()}"
285
  commercial_phone = f"Tél: {commercial_info.get(data.commercial.lower(), '')}"
286
-
287
  pdf.drawString(MARGIN + 10, 30, commercial_text)
288
- #draw under the commercial text :
289
  pdf.drawString(MARGIN + 10, 20, commercial_phone)
290
  footer_contact = "Tél: 05 24 01 55 54 Fax : 05 24 01 55 29 E-mail : [email protected]"
291
  pdf.drawCentredString(page_width / 2 + 10, 20, footer_contact)
292
- ## add the time and page number
293
-
294
  pdf.drawString(page_width - 100, 30, f"Date: {datetime.datetime.now().strftime('%d/%m/%Y')}")
295
  pdf.drawString(page_width - 100, 20, f"Page {pdf.getPageNumber()}/{pdf.getPageNumber()}")
296
  else:
297
  footer_contact = "Tél: 05 24 01 55 54 Fax : 05 24 01 55 29 E-mail : [email protected]"
298
  pdf.drawCentredString(page_width / 2 + 10, 20, footer_contact)
299
- pdf.drawString(page_width - 100, 30, f"Date: {datetime.datetime().now.strftime('%d/%m/%Y')}")
300
  pdf.drawString(page_width - 100, 20, f"Page {pdf.getPageNumber()}/{pdf.getPageNumber()}")
301
 
302
  pdf.save()
 
27
  MARGIN = 30
28
  LINE_HEIGHT = 20
29
  BOX_PADDING = 10
30
+ STANDARD_FONT_SIZE = 10
31
+ BOTTOM_MARGIN = 50 # Minimum margin at the bottom of the page
32
 
33
  # Helper function to draw centered text
34
  def draw_centered_text(pdf, text, x, y, width, font="Helvetica", size=STANDARD_FONT_SIZE):
 
55
  pdf.drawImage(logo_path, MARGIN, top_margin + 30, width=100, height=60)
56
 
57
  # Right side: DEVIS and Client Box - moved far right and up
58
+ pdf.setFont("Helvetica-Bold", 36)
59
  devis_text = "DEVIS"
60
  devis_width = pdf.stringWidth(devis_text, "Helvetica-Bold", 36)
61
  devis_x = page_width - devis_width - MARGIN - 10
 
123
  table_x = (page_width - total_width) / 2 # Center table
124
  draw_box(pdf, table_x, table_y, total_width, LINE_HEIGHT, fill_color=HEADER_BLUE)
125
  pdf.setFillColorRGB(*WHITE)
126
+
127
+ current_x = table_x
 
 
128
  for title, width in headers:
129
  draw_box(pdf, current_x, table_y, width, LINE_HEIGHT)
130
  pdf.setFillColorRGB(*WHITE)
131
  draw_centered_text(pdf, title, current_x, table_y + 6, width)
132
  current_x += width
133
 
 
134
  # Draw sections and items
135
  current_y = table_y - LINE_HEIGHT - 10
136
 
 
 
137
  def draw_section_header2(title):
138
  nonlocal current_y
139
  draw_box(pdf, table_x, current_y, total_width, LINE_HEIGHT, fill_color=WHITE)
140
+ pdf.setFont("Helvetica-Bold", 9)
141
+ pdf.setFillColorRGB(*BLACK)
 
 
 
 
 
 
142
  pdf.drawString(table_x + BOX_PADDING, current_y + 6, title)
 
143
  current_y -= LINE_HEIGHT
144
  pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
145
 
146
  def format_currency(value):
 
147
  return "{:,.2f}".format(value).replace(",", " ")
148
 
 
149
  def draw_item_row(item, indent=False):
150
  nonlocal current_y
151
  pdf.setFillColorRGB(*BLACK)
 
159
  (str(item.quantity), 50),
160
  (f"{item.length:.2f}", 60),
161
  (f"{format_currency(item.unit_price)}", 60),
162
+ (f"{format_currency(item.total_price)} DH", 170)
163
  ]
164
 
165
  for i, (value, width) in enumerate(cells):
166
  draw_box(pdf, current_x, current_y, width, LINE_HEIGHT)
167
 
168
+ if i == len(cells) - 1:
169
+ pdf.setFont("Helvetica-Bold", STANDARD_FONT_SIZE)
170
  else:
171
+ pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
172
 
173
  if isinstance(value, str) and value.startswith(" "):
174
  pdf.drawString(current_x + 20, current_y + 6, value.strip())
 
177
 
178
  current_x += width
179
 
180
+ pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
181
  current_y -= LINE_HEIGHT
182
 
183
+ # Check if we need a new page
184
+ if current_y < BOTTOM_MARGIN:
185
+ pdf.showPage()
186
+ current_y = page_height - MARGIN
187
+ # Redraw headers on the new page
188
+ draw_box(pdf, table_x, current_y, total_width, LINE_HEIGHT, fill_color=HEADER_BLUE)
189
+ pdf.setFillColorRGB(*WHITE)
190
+ current_x = table_x
191
+ for title, width in headers:
192
+ draw_box(pdf, current_x, current_y, width, LINE_HEIGHT)
193
+ pdf.setFillColorRGB(*WHITE)
194
+ draw_centered_text(pdf, title, current_x, current_y + 6, width)
195
+ current_x += width
196
+ current_y -= LINE_HEIGHT
197
 
198
  # Draw sections
199
  sections = [
 
204
  ]
205
 
206
  for section_title, keyword in sections:
 
 
207
  draw_section_header2(section_title)
208
  items = [i for i in data.items if keyword in i.description]
209
  for item in items:
 
216
  pdf.rect(20, current_y - nb_box_height, nb_box_width, nb_box_height, stroke=1)
217
  pdf.setFont("Helvetica-Bold", STANDARD_FONT_SIZE)
218
  pdf.drawString(30, current_y - nb_box_height + 60, "NB:")
219
+
220
  # Add the new text
221
  pdf.setFont("Helvetica", STANDARD_FONT_SIZE)
222
  nb_text = "Toute modification apportée aux plans BA initialement fournis, entraine automatiquement la modification de ce devis."
 
223
  words = nb_text.split()
224
  lines = []
225
  current_line = []
226
+
227
  for word in words:
228
  current_line.append(word)
 
229
  if pdf.stringWidth(' '.join(current_line), "Helvetica", STANDARD_FONT_SIZE) > nb_box_width - 20:
230
+ current_line.pop()
231
  lines.append(' '.join(current_line))
232
  current_line = [word]
233
+
234
  if current_line:
235
  lines.append(' '.join(current_line))
236
+
 
237
  for i, line in enumerate(lines):
238
  pdf.drawString(30, current_y - nb_box_height + 45 - (i * 10), line)
239
 
240
  # ADD text after the NB box
241
  pdf.setFont("Helvetica-Bold", 9)
242
+ pdf.drawString(30, current_y - nb_box_height - 15, "Validité du devis : 1 mois")
243
+
 
244
  # Totals section
 
245
  pdf.setFont("Helvetica-Bold", 12)
246
  current_y -= 20
247
  totals_table_width = 300
 
264
  footer_text = "Douar Ait Laarassi Tidili, Cercle El Kelâa, Route de Safi, Km 14-40000 Marrakech"
265
  pdf.drawCentredString(page_width / 2 + 20, 30, footer_text)
266
 
267
+ # Add commercial info to footer
 
268
  commercial_info = dict(
269
+ salah="06 62 29 99 78",
270
+ khaled="06 66 24 80 94",
271
+ ismail="06 66 24 50 15",
272
+ jamal="06 70 08 36 50"
273
  )
274
+
 
275
  if data.commercial and data.commercial.lower() != 'divers':
276
  commercial_text = f"Commercial: {data.commercial.upper()}"
277
  commercial_phone = f"Tél: {commercial_info.get(data.commercial.lower(), '')}"
 
278
  pdf.drawString(MARGIN + 10, 30, commercial_text)
 
279
  pdf.drawString(MARGIN + 10, 20, commercial_phone)
280
  footer_contact = "Tél: 05 24 01 55 54 Fax : 05 24 01 55 29 E-mail : [email protected]"
281
  pdf.drawCentredString(page_width / 2 + 10, 20, footer_contact)
 
 
282
  pdf.drawString(page_width - 100, 30, f"Date: {datetime.datetime.now().strftime('%d/%m/%Y')}")
283
  pdf.drawString(page_width - 100, 20, f"Page {pdf.getPageNumber()}/{pdf.getPageNumber()}")
284
  else:
285
  footer_contact = "Tél: 05 24 01 55 54 Fax : 05 24 01 55 29 E-mail : [email protected]"
286
  pdf.drawCentredString(page_width / 2 + 10, 20, footer_contact)
287
+ pdf.drawString(page_width - 100, 30, f"Date: {datetime.datetime.now().strftime('%d/%m/%Y')}")
288
  pdf.drawString(page_width - 100, 20, f"Page {pdf.getPageNumber()}/{pdf.getPageNumber()}")
289
 
290
  pdf.save()
sql_app.db CHANGED
Binary files a/sql_app.db and b/sql_app.db differ