import frappe from reportlab.lib.pagesizes import letter from reportlab.lib.units import inch from reportlab.lib.colors import HexColor from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, HRFlowable from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.enums import TA_CENTER from reportlab.lib import colors import sqlite3 import io DB_PATH = '/opt/eim/eim.db' DARK_BLUE = HexColor('#2F5496') LIGHT_BLUE = HexColor('#D6E4F0') GRAY = HexColor('#666666') def get_device_counts(pallet_number): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row cur = conn.cursor() cur.execute('SELECT device_type, COUNT(*) as count FROM devices WHERE pallet_number = ? GROUP BY device_type', (pallet_number,)) counts = {row['device_type']: row['count'] for row in cur.fetchall()} conn.close() return counts @frappe.whitelist() def generate_cor(pallet_number): pallet = frappe.get_doc('Pallet', pallet_number) device_counts = get_device_counts(pallet_number) output = io.BytesIO() doc = SimpleDocTemplate(output, pagesize=letter, topMargin=0.75*inch, bottomMargin=0.75*inch, leftMargin=0.75*inch, rightMargin=0.75*inch) styles = getSampleStyleSheet() title_style = ParagraphStyle('CertTitle', parent=styles['Title'], fontSize=20, textColor=DARK_BLUE, spaceAfter=6, alignment=TA_CENTER) body_style = ParagraphStyle('CertBody', parent=styles['Normal'], fontSize=11, spaceAfter=4) section_style = ParagraphStyle('SectionHeader', parent=styles['Heading2'], fontSize=14, textColor=DARK_BLUE, spaceBefore=12, spaceAfter=6) elements = [] elements.append(Paragraph('CERTIFICATE OF RECYCLING', title_style)) elements.append(Spacer(1, 6)) intro = 'Full Circle Electronics AZ, LLC (dba Westech Recyclers) certifies that the materials submitted for recycling are received and will be properly recycled in accordance with all state and federal recycling regulations and in accordance with the R2 Standard.' elements.append(Paragraph(intro, body_style)) elements.append(Spacer(1, 8)) elements.append(Paragraph('Materials Submitted by:', section_style)) pallet_data = [ ['Company:', pallet.company_name or pallet.customer_number or 'N/A'], ['Pallet Number:', pallet.pallet_number or pallet_number], ['Date Received:', str(pallet.received_date or 'N/A')], ['Weight:', str(pallet.inbound_weight or 'N/A') + ' lbs'], ['Technician:', pallet.tester or 'N/A'], ] pallet_table = Table(pallet_data, colWidths=[1.5*inch, 4*inch]) pallet_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (0, -1), LIGHT_BLUE), ('TEXTCOLOR', (0, 0), (0, -1), DARK_BLUE), ('FONTNAME', (0, 0), (0, -1), 'Helvetica-Bold'), ('FONTSIZE', (0, 0), (-1, -1), 10), ('ALIGN', (0, 0), (0, -1), 'RIGHT'), ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), ('TOPPADDING', (0, 0), (-1, -1), 4), ('BOTTOMPADDING', (0, 0), (-1, -1), 4), ])) elements.append(pallet_table) elements.append(Spacer(1, 12)) if device_counts: elements.append(Paragraph('Device Summary:', section_style)) device_data = [['Device Type', 'Count']] for dtype, count in sorted(device_counts.items()): device_data.append([dtype or 'Unknown', str(count)]) device_table = Table(device_data, colWidths=[3*inch, 2*inch]) device_table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (-1, 0), DARK_BLUE), ('TEXTCOLOR', (0, 0), (-1, 0), colors.white), ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), ('GRID', (0, 0), (-1, -1), 0.5, colors.grey), ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, HexColor('#F2F2F2')]), ])) elements.append(device_table) elements.append(Spacer(1, 12)) elements.append(HRFlowable(width='100%', thickness=1, color=DARK_BLUE, spaceBefore=12)) footer = Paragraph('Full Circle Electronics AZ, LLC | 220 S 9th St Phoenix, AZ 85034 | www.westechrecyclers.com | 602.256.7626', ParagraphStyle('Footer', parent=styles['Normal'], fontSize=9, textColor=GRAY, alignment=TA_CENTER)) elements.append(Spacer(1, 6)) elements.append(footer) doc.build(elements) output.seek(0) frappe.response.filename = 'COR_' + pallet_number + '.pdf' frappe.response.filecontent = output.getvalue() frappe.response.type = 'download' frappe.response.display_content_as = 'attachment'