File size: 27,945 Bytes
2f7e39e
 
 
 
 
 
 
 
402feb9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f7e39e
cb24c49
 
 
bdba181
 
 
 
 
 
 
402feb9
 
bdba181
 
 
 
 
 
 
 
 
 
cb24c49
bdba181
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402feb9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bdba181
 
 
 
 
 
 
 
 
 
 
 
 
0c7025c
bdba181
 
 
 
0c7025c
bdba181
0c7025c
bdba181
 
 
 
 
 
 
0c7025c
 
bdba181
 
 
0c7025c
 
 
 
 
 
 
 
bdba181
 
 
 
 
 
 
cb24c49
 
 
402feb9
cb24c49
402feb9
bdba181
cb24c49
0c7025c
 
 
cb24c49
bdba181
 
cb24c49
 
bdba181
 
0c7025c
cb24c49
bdba181
 
 
0c7025c
cb24c49
 
 
0c7025c
cb24c49
0c7025c
 
cb24c49
 
 
 
0c7025c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cb24c49
 
 
0c7025c
bdba181
 
0c7025c
 
 
 
 
 
 
 
 
 
cb24c49
0c7025c
 
 
 
 
bdba181
0c7025c
bdba181
 
402feb9
0c7025c
402feb9
 
0c7025c
402feb9
 
 
 
0c7025c
402feb9
 
 
 
 
0c7025c
 
402feb9
0c7025c
402feb9
0c7025c
cb24c49
402feb9
cb24c49
bdba181
 
cb24c49
bdba181
cb24c49
bdba181
 
 
cb24c49
bdba181
cb24c49
bdba181
 
402feb9
bdba181
cb24c49
 
bdba181
402feb9
 
 
cb24c49
bdba181
cb24c49
402feb9
cb24c49
 
 
bdba181
 
 
402feb9
 
 
 
 
 
cb24c49
 
 
0c7025c
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
import subprocess
import sys
# Install openpyxl if not available
try:
    import openpyxl
except ImportError:
    subprocess.check_call([sys.executable, "-m", "pip", "install", "openpyxl"])
    import openpyxl
# Install additional dependencies for PDF generation
try:
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import A4
    from reportlab.lib.units import inch
    from reportlab.lib.colors import HexColor
    from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, Image
    from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
    from reportlab.lib import colors
except ImportError:
    subprocess.check_call([sys.executable, "-m", "pip", "install", "reportlab"])
    from reportlab.pdfgen import canvas
    from reportlab.lib.pagesizes import A4
    from reportlab.lib.units import inch
    from reportlab.lib.colors import HexColor
    from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, Image
    from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
    from reportlab.lib import colors

import gradio as gr
import pandas as pd
import tempfile
from datetime import datetime, timedelta
import openpyxl
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
from openpyxl.drawing.image import Image as ExcelImage
import random
import base64
from io import BytesIO
import os
import zipfile

def generate_random_invoice_number():
    """Generate random 6-digit invoice number"""
    return str(random.randint(100000, 999999))

def create_professional_individual_invoice(workbook, employee_data, start_date, end_date):
    """Create individual invoice sheet with professional Glimmr template formatting and logo"""
    # --- PASTE YOUR CLEANED BASE64 STRING HERE ---
    GLIMMR_LOGO_BASE64 = """
    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
    """
    ws = workbook.create_sheet(title=employee_data['Row Labels'])
    column_widths = {'A': 25, 'B': 15, 'C': 15, 'D': 15, 'E': 15, 'F': 12, 'G': 12, 'H': 12, 'I': 12}
    for col, width in column_widths.items():
        ws.column_dimensions[col].width = width
    try:
        if GLIMMR_LOGO_BASE64.strip() and "PASTE" not in GLIMMR_LOGO_BASE64 and "YOUR BASE64" not in GLIMMR_LOGO_BASE64:
            logo_data = base64.b64decode(GLIMMR_LOGO_BASE64.strip())
            logo_stream = BytesIO(logo_data)
            img = ExcelImage(logo_stream)
            img.width = 200
            img.height = 100
            ws.add_image(img, 'A1')
    except Exception as e:
        print(f"Logo embedding failed: {e}")
    # Company name
    ws['A6'] = 'Glimmr Ltd'
    ws['A6'].font = Font(bold=True, size=16)
    # --- CORRECTED HEADER STRUCTURE TO MATCH ORIGINAL ---
    # Main header spanning wider (extended one cell left)
    ws.merge_cells('E2:I2')
    ws['E2'] = 'CLEANER INVOICE'
    ws['E2'].font = Font(bold=True, size=14, color='FFFFFF')
    ws['E2'].alignment = Alignment(horizontal='center', vertical='center')
    ws['E2'].fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
    # Invoice # header (left side)
    ws.merge_cells('E6:F6')
    ws['E6'] = 'Invoice #'
    ws['E6'].font = Font(bold=True, color='FFFFFF')
    ws['E6'].fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
    ws['E6'].alignment = Alignment(horizontal='center', vertical='center')
    # Date header (right side)
    ws.merge_cells('G6:I6')
    ws['G6'] = 'Date'
    ws['G6'].font = Font(bold=True, color='FFFFFF')
    ws['G6'].fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
    ws['G6'].alignment = Alignment(horizontal='center', vertical='center')
    # Invoice # value (below header)
    ws.merge_cells('E7:F7')
    ws['E7'] = generate_random_invoice_number()
    ws['E7'].alignment = Alignment(horizontal='center', vertical='center')
    # Date value (below header)
    ws.merge_cells('G7:I7')
    ws['G7'] = datetime.now().strftime('%d/%m/%y')
    ws['G7'].alignment = Alignment(horizontal='center', vertical='center')
    # Company address
    ws['A8'] = '108 Westbourne Terrace'
    ws['A9'] = 'London'
    ws['A10'] = 'W2 6QJ'
    ws['A11'] = 'support@glimmr.co.uk'
    ws['A12'] = '020 8158 8505'
    # Payee information
    ws['A14'] = "Payee's Name"
    ws['A14'].font = Font(bold=True)
    ws['A15'] = employee_data['Row Labels']
    ws['A15'].font = Font(size=11)
    # Payment period header
    ws.merge_cells('A20:D20')
    ws['A20'] = 'Payment Period'
    ws['A20'].font = Font(bold=True, color='FFFFFF')
    ws['A20'].fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
    ws['A20'].alignment = Alignment(horizontal='center')
    # Table headers - adjusted positions
    ws['E20'] = 'Hours'
    ws['F20'] = 'Β£ / Hour'
    ws.merge_cells('G20:I20')
    ws['G20'] = 'Amount'
    for cell in ['E20', 'F20', 'G20']:
        ws[cell].font = Font(bold=True, color='FFFFFF')
        ws[cell].fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
        ws[cell].alignment = Alignment(horizontal='center')
    # Payment period data with center alignment
    ws.merge_cells('A21:D21')
    ws['A21'] = f"{start_date} to {end_date}"
    ws['A21'].alignment = Alignment(horizontal='center')
    ws['E21'] = employee_data['Sum of Hours Worked']
    ws['F21'] = f"Β£  {employee_data['Average of Hourly Rate']:.2f}"
    ws.merge_cells('G21:I21')
    ws['G21'] = f"Β£  {employee_data['Sum of Total']:.2f}"
    # Total sections - adjusted positions
    ws.merge_cells('F24:G24')
    ws['F24'] = 'Total Labor'
    ws['F24'].font = Font(bold=True)
    ws.merge_cells('H24:I24')
    ws['H24'] = f"Β£  {employee_data['Sum of Total']:.2f}"
    ws.merge_cells('F26:G26')
    ws['F26'] = 'Tax (Withholding)'
    # Payment due date and total
    due_date = (datetime.strptime(end_date, '%Y-%m-%d') + timedelta(days=1)).strftime('%d/%m/%Y')
    ws['A27'] = f'Payment is due on {due_date}'
    ws.merge_cells('F27:G27')
    ws['F27'] = 'TOTAL'
    ws['F27'].font = Font(bold=True)
    ws.merge_cells('H27:I27')
    ws['H27'] = f"Β£  {employee_data['Sum of Total']:.2f}"
    ws['H27'].font = Font(bold=True)
    # Comments section
    ws['A28'] = 'Comments or Special Instructions:'
    ws['A28'].font = Font(bold=True)
    ws['A29'] = 'Payments to be made every other Saturday (or as close to these days).'
    ws['A30'] = 'Please let us know immediately if there are any issues.'
    # Apply borders - updated ranges
    thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
    # Border for payment period table
    for row in range(20, 22):
        for col in range(1, 10):
            ws.cell(row=row, column=col).border = thin_border
    # Border for header section
    for row in range(6, 8):
        for col_letter in ['E', 'F', 'G', 'H', 'I']:
            ws[f'{col_letter}{row}'].border = thin_border

def create_individual_pdf_invoice(employee_data, start_date, end_date, output_dir):
    """Create individual PDF invoice for each cleaner"""
    GLIMMR_LOGO_BASE64 = """
    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
    """
    clean_name = "".join(c for c in employee_data['Row Labels'] if c.isalnum() or c in (' ', '-', '_')).rstrip()
    clean_name = clean_name.replace(' ', '_')
    start_dt = datetime.strptime(start_date, '%Y-%m-%d')
    end_dt = datetime.strptime(end_date, '%Y-%m-%d')
    date_period = f"{start_dt.strftime('%d%b%Y')}-{end_dt.strftime('%d%b%Y')}"
    filename = f"{clean_name}_{date_period}_Glimmr_Invoice.pdf"
    filepath = os.path.join(output_dir, filename)
    doc = SimpleDocTemplate(filepath, pagesize=A4, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18)
    story = []
    try:
        if GLIMMR_LOGO_BASE64.strip():
            logo_data = base64.b64decode(GLIMMR_LOGO_BASE64.strip())
            logo_stream = BytesIO(logo_data)
            logo_img = Image(logo_stream)
            logo_img.drawWidth = 2*inch
            logo_img.drawHeight = 1*inch
            story.append(logo_img)
            story.append(Spacer(1, 12))
    except Exception as e:
        print(f"PDF Logo embedding failed: {e}")
    styles = getSampleStyleSheet()
    title_style = ParagraphStyle('CustomTitle', parent=styles['Heading1'], fontSize=18, spaceAfter=30, textColor=HexColor('#4472C4'), alignment=1)
    header_style = ParagraphStyle('CustomHeader', parent=styles['Heading2'], fontSize=14, spaceAfter=12, textColor=HexColor('#4472C4'))
    normal_style = styles['Normal']
    story.append(Paragraph('<b>Glimmr Ltd</b>', title_style))
    address_text = "108 Westbourne Terrace<br/>London<br/>W2 6QJ<br/>support@glimmr.co.uk<br/>020 8158 8505"
    story.append(Paragraph(address_text, normal_style))
    story.append(Spacer(1, 20))
    invoice_number = generate_random_invoice_number()
    current_date = datetime.now().strftime('%d/%m/%Y')
    invoice_header_data = [['CLEANER INVOICE', ''], ['Invoice #: ' + invoice_number, 'Date: ' + current_date]]
    invoice_header_table = Table(invoice_header_data, colWidths=[3*inch, 3*inch])
    invoice_header_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), HexColor('#4472C4')),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, 0), 14),
        ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
        ('FONTSIZE', (0, 1), (-1, -1), 12),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 12),
        ('GRID', (0, 0), (-1, -1), 1, colors.black)
    ]))
    story.append(invoice_header_table)
    story.append(Spacer(1, 20))
    story.append(Paragraph('<b>Payee\'s Name:</b>', header_style))
    story.append(Paragraph(employee_data['Row Labels'], normal_style))
    story.append(Spacer(1, 20))
    due_date = (datetime.strptime(end_date, '%Y-%m-%d') + timedelta(days=1)).strftime('%d/%m/%Y')
    payment_data = [
        ['Payment Period', 'Hours', 'Β£ / Hour', 'Amount'],
        [f"{start_date} to {end_date}",
         str(employee_data['Sum of Hours Worked']),
         f"Β£ {employee_data['Average of Hourly Rate']:.2f}",
         f"Β£ {employee_data['Sum of Total']:.2f}"]
    ]
    payment_table = Table(payment_data, colWidths=[2*inch, 1*inch, 1*inch, 1.5*inch])
    payment_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), HexColor('#4472C4')),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
        ('FONTSIZE', (0, 0), (-1, -1), 10),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
        ('GRID', (0, 0), (-1, -1), 1, colors.black)
    ]))
    story.append(payment_table)
    story.append(Spacer(1, 20))
    total_data = [
        ['Total Labor', f"Β£ {employee_data['Sum of Total']:.2f}"],
        ['Tax (Withholding)', 'Β£ 0.00'],
        ['TOTAL', f"Β£ {employee_data['Sum of Total']:.2f}"]
    ]
    total_table = Table(total_data, colWidths=[3*inch, 2*inch])
    total_table.setStyle(TableStyle([
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('FONTNAME', (0, 0), (-1, 1), 'Helvetica'),
        ('FONTNAME', (0, 2), (-1, 2), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, -1), 12),
        ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
        ('GRID', (0, 0), (-1, -1), 1, colors.black),
        ('BACKGROUND', (0, 2), (-1, 2), colors.lightgrey)
    ]))
    story.append(total_table)
    story.append(Spacer(1, 20))
    story.append(Paragraph(f'<b>Payment is due on {due_date}</b>', normal_style))
    story.append(Spacer(1, 20))
    story.append(Paragraph('<b>Comments or Special Instructions:</b>', header_style))
    story.append(Paragraph('Payments to be made every other Saturday (or as close to these days).', normal_style))
    story.append(Paragraph('Please let us know immediately if there are any issues.', normal_style))
    doc.build(story)
    return filepath

def create_invoices_from_template_with_logo(report_data, start_date, end_date):
    wb = openpyxl.Workbook()
    wb.remove(wb.active)
    # Create Payments sheet
    payments_ws = wb.create_sheet(title="Payments")
    payments_ws['C1'] = 'DO NOT ALTER THIS TEMPLATE'
    payments_ws['C1'].font = Font(bold=True)
    headers = ['First Name', 'Last Name', 'Stripe Name', 'Reference', 'Amount (GBP)', 'Remarks']
    for col, header in enumerate(headers, 1):
        cell = payments_ws.cell(row=2, column=col, value=header)
        cell.font = Font(bold=True)
    for row_idx, employee in enumerate(report_data[report_data['Row Labels'] != 'Grand Total'].itertuples(), 3):
        name_parts = employee[1].split(' ', 1)
        first_name = name_parts[0] if name_parts else ''
        last_name = name_parts[1] if len(name_parts) > 1 else ''
        payments_ws.cell(row=row_idx, column=1, value=first_name)
        payments_ws.cell(row=row_idx, column=2, value=last_name)
        payments_ws.cell(row=row_idx, column=3, value=employee[1])
        payments_ws.cell(row=row_idx, column=4, value='')
        payments_ws.cell(row=row_idx, column=5, value=employee[4]) # Sum of Total
        payments_ws.cell(row=row_idx, column=6, value='')
    # Create Pivot sheet
    pivot_ws = wb.create_sheet(title="Pivot")
    pivot_ws['A1'] = f"{start_date} to {end_date}"
    pivot_ws['A1'].font = Font(bold=True)
    due_date = (datetime.strptime(end_date, '%Y-%m-%d') + timedelta(days=1)).strftime('%d/%m/%Y')
    pivot_ws['A2'] = f"Payment is due on {due_date}"
    # MODIFIED: Updated header name as requested
    pivot_headers = ['Name', 'Fixed Hourly Rate(without TIP)', 'Average of Hourly Rate', 'Sum of Hours Worked', 'Sum of Total']
    for col, header in enumerate(pivot_headers, 1):
        cell = pivot_ws.cell(row=3, column=col, value=header)
        cell.font = Font(bold=True)
    for row_idx, employee in enumerate(report_data.itertuples(index=False), 4):
        # The tuple index corresponds to the column order in the final_invoice DataFrame
        # [0]: Row Labels, [1]: Avg Rate, [2]: Sum Hours, [3]: Sum Total, [4]: Fixed Rate
        pivot_ws.cell(row=row_idx, column=1, value=employee[0])  # Name
        pivot_ws.cell(row=row_idx, column=2, value=employee[4])  # Fixed Hourly Rate(without TIP)
        pivot_ws.cell(row=row_idx, column=3, value=employee[1])  # Average of Hourly Rate
        pivot_ws.cell(row=row_idx, column=4, value=employee[2])  # Sum of Hours Worked
        pivot_ws.cell(row=row_idx, column=5, value=employee[3])  # Sum of Total
    # Create individual invoice sheets
    individual_employees = report_data[report_data['Row Labels'] != 'Grand Total']
    for _, employee in individual_employees.iterrows():
        create_professional_individual_invoice(wb, employee, start_date, end_date)
    return wb

def process_invoice_files_with_professional_excel(file1, file2, file3, bookings_file, start_date, end_date):
    try:
        employee_files = [f for f in [file1, file2, file3] if f is not None]
        if not employee_files:
            return None, None, "❗ **Error:** At least one Employee CSV must be uploaded."
        if bookings_file is None:
            return None, None, "❗ **Error:** Bookings CSV is required."
        employee_dfs = [pd.read_csv(ef.name) for ef in employee_files]
        all_employee_data = pd.concat(employee_dfs, ignore_index=True)
        # Add the new Fixed Hour Rate column
        all_employee_data['Fixed Hour Rate'] = all_employee_data['Hourly Rate']

        pdf_bookings = pd.read_csv(bookings_file.name)
        all_employee_data['Date'] = pd.to_datetime(all_employee_data['Date'], dayfirst=True, errors='coerce')
        pdf_bookings['Date'] = pd.to_datetime(pdf_bookings['Date'], dayfirst=True, errors='coerce')
        start_date_dt = pd.to_datetime(start_date)
        end_date_dt = pd.to_datetime(end_date)
        filtered_data = all_employee_data[(all_employee_data['Date'] >= start_date_dt) & (all_employee_data['Date'] <= end_date_dt)].copy()
        filtered_bookings = pdf_bookings[(pdf_bookings['Date'] >= start_date_dt) & (pdf_bookings['Date'] <= end_date_dt)].copy()

        def extract_employee_name(team_string):
            if pd.isna(team_string): return None
            name = team_string.split(',')[0].strip()
            return name.split('(')[0].strip()

        filtered_bookings['Employee_Name'] = filtered_bookings['Teams Assigned  (without IDs)'].apply(extract_employee_name)
        tips_summary = filtered_bookings.groupby('Employee_Name')['Tip'].sum().reset_index()
        tips_with_amount = tips_summary[tips_summary['Tip'] > 0]

        if not tips_with_amount.empty:
            # Add Fixed Hour Rate to tip rows as 0
            tip_rows = [{'Name': row['Employee_Name'], 'Hourly Rate': 0, 'Hours Worked': 0, 'Total': row['Tip'], 'Fixed Hour Rate': 0} for _, row in tips_with_amount.iterrows()]
            tip_df = pd.DataFrame(tip_rows)
            final_data = pd.concat([filtered_data, tip_df], ignore_index=True)
        else:
            final_data = filtered_data.copy()

        # Update aggregation to include Fixed Hour Rate
        report = final_data.groupby('Name').agg({
            'Hourly Rate': 'mean',
            'Hours Worked': 'sum',
            'Total': 'sum',
            'Fixed Hour Rate': 'first' # Use 'first' to get the original rate
        }).reset_index()

        # MODIFIED: Update column renaming as requested
        report = report.rename(columns={
            'Name': 'Row Labels',
            'Hourly Rate': 'Average of Hourly Rate',
            'Hours Worked': 'Sum of Hours Worked',
            'Total': 'Sum of Total',
            'Fixed Hour Rate': 'Fixed Hourly Rate(without TIP)'
        })

        report['Average of Hourly Rate'] = report['Average of Hourly Rate'].round(8)
        report['Sum of Hours Worked'] = report['Sum of Hours Worked'].round(2)
        report['Sum of Total'] = report['Sum of Total'].round(2)

        total_hours = report['Sum of Hours Worked'].sum()
        total_sum = report['Sum of Total'].sum()

        # MODIFIED: Update Grand Total DataFrame to use new column name
        grand_total = pd.DataFrame({
            'Row Labels': ['Grand Total'],
            'Average of Hourly Rate': [total_sum / total_hours if total_hours > 0 else 0],
            'Sum of Hours Worked': [total_hours],
            'Sum of Total': [total_sum],
            'Fixed Hourly Rate(without TIP)': [None]
        })

        final_invoice = pd.concat([report, grand_total], ignore_index=True)

        # Reorder columns for the create_invoices function to process correctly
        # The order here determines the order in `itertuples`
        final_invoice = final_invoice[['Row Labels', 'Average of Hourly Rate', 'Sum of Hours Worked', 'Sum of Total', 'Fixed Hourly Rate(without TIP)']]

        wb = create_invoices_from_template_with_logo(final_invoice, start_date, end_date)

        with tempfile.NamedTemporaryFile(delete=False, suffix='.xlsx') as tmp:
            wb.save(tmp.name)
            temp_excel_file_path = tmp.name

        # Create PDF files for individual cleaners
        pdf_temp_dir = tempfile.mkdtemp()
        individual_employees = final_invoice[final_invoice['Row Labels'] != 'Grand Total']
        pdf_files = []
        for _, employee in individual_employees.iterrows():
            pdf_path = create_individual_pdf_invoice(employee, start_date, end_date, pdf_temp_dir)
            pdf_files.append(pdf_path)

        # Create a ZIP file containing all PDFs
        zip_temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
        with zipfile.ZipFile(zip_temp_file.name, 'w') as zipf:
            for pdf_file in pdf_files:
                zipf.write(pdf_file, os.path.basename(pdf_file))

        individual_employees_count = len(final_invoice[final_invoice['Row Labels'] != 'Grand Total'])
        summary_text = f"πŸŽ‰ **Professional Invoice Generated Successfully!**\n\n- βœ… **Individual Professional Invoices** for each employee ({individual_employees_count} Excel sheets)\n- βœ… **Individual PDF Invoices** for each cleaner ({individual_employees_count} PDFs)\n- βœ… **Payments Summary & Pivot Data** sheets included\n- βœ… **Glimmr Logo & Branding** applied\n\n**Summary for {start_date} to {end_date}:**\n- Total hours: {total_hours:.2f}\n- Total amount: Β£{total_sum:.2f}\n\n**Files Generated:**\n- Excel file with all data and individual sheets\n- ZIP file containing individual PDF invoices for each cleaner"

        return temp_excel_file_path, zip_temp_file.name, summary_text

    except Exception as e:
        return None, None, f"❌ **An error occurred:**\n\n{str(e)}\n\nPlease check your input files and date formats (YYYY-MM-DD)."

with gr.Blocks(title="Complete Professional Invoice Generator", theme=gr.themes.Soft()) as interface:
    gr.Markdown("# 🧾 Complete Professional Invoice Generator for Launch27 Data")
    with gr.Row():
        with gr.Column(scale=2):
            gr.Markdown("### πŸ“ Upload CSV Files")
            file1 = gr.File(label="Upload 1.csv (Optional Employee Data)", file_types=[".csv"])
            file2 = gr.File(label="Upload 2.csv (Optional Employee Data)", file_types=[".csv"])
            file3 = gr.File(label="Upload 3.csv (Optional Employee Data)", file_types=[".csv"])
            bookings_file = gr.File(label="Upload Bookings CSV (Required)", file_types=[".csv"])
        with gr.Column(scale=1):
            gr.Markdown("### πŸ“… Select Date Range")
            start_date = gr.Textbox(label="Start Date (YYYY-MM-DD)", value="2025-08-09")
            end_date = gr.Textbox(label="End Date (YYYY-MM-DD)", value="2025-08-22")
            process_btn = gr.Button("πŸš€ Generate Professional Invoice Excel & PDFs", variant="primary", size="lg")
    gr.Markdown("---")
    gr.Markdown("### πŸ“‹ Results")
    with gr.Row():
        summary_output = gr.Markdown(label="Summary")
    with gr.Row():
        download_excel_file = gr.File(label="πŸ“₯ Download Professional Invoice Excel (.xlsx)")
        download_pdf_zip = gr.File(label="πŸ“₯ Download Individual PDF Invoices (.zip)")
    process_btn.click(
        fn=process_invoice_files_with_professional_excel,
        inputs=[file1, file2, file3, bookings_file, start_date, end_date],
        outputs=[download_excel_file, download_pdf_zip, summary_output]
    )
    with gr.Accordion("πŸ“– How to Use", open=False):
        gr.Markdown("""
1.  **Upload Files**: Upload 1-3 employee CSVs and the required bookings CSV.
2.  **Select Dates**: Enter the date range in YYYY-MM-DD format.
3.  **Generate**: Click the button.
4.  **Download**: Your complete Excel file and ZIP of individual PDF invoices will appear for download.

**New Features:**
- Individual PDF invoices for each cleaner with the naming convention: `Cleaner_Name_Dateperiod_Glimmr_Invoice.pdf`
- All PDF files are packaged in a convenient ZIP file for download
- Professional PDF formatting matching the Excel invoice design
        """)

if __name__ == "__main__":
    interface.launch(debug=True)