diff --git a/app/bookings/routes.py b/app/bookings/routes.py index 668c62637d988d55648109128856a6d73e3c8710..ec50e6cd13608ea4438ec89291cd6b50ba2e23b4 100644 --- a/app/bookings/routes.py +++ b/app/bookings/routes.py @@ -1,9 +1,9 @@ -from flask import render_template, redirect, url_for, request, jsonify, session, flash, g +from flask import render_template, redirect, url_for, request, jsonify, session, flash, g, send_file from app.bookings import bp from app.models import Listings, Bookings, ListingAvailability from app import db from app.logger import error_logger -from app.main.utils import calculate_discount, pretty_time +from app.main.utils import calculate_discount, pretty_time, create_receipt, create_plane_ticket import json from datetime import datetime from app import user_permission, permission_required @@ -209,7 +209,7 @@ def listing(id): ) # This route should be used after show_listing if used internally as this clears the ajax parameters before redirecting the user -@bp.route('/payment/successful/<int:id>', methods=['GET']) +@bp.route('/checkout/success/<int:id>', methods=['GET']) @permission_required(user_permission) def payment_complete(id): @@ -220,7 +220,7 @@ def payment_complete(id): return redirect(url_for('main.index')) return render_template( - 'bookings/payment_success.html', + 'bookings/payment_success.html', id=booking.id ) @bp.route('/checkout_post', methods=['POST']) @@ -353,3 +353,25 @@ def process_images(listings): item.main_image_url = url_for('main.upload_file', filename='booking_image_not_found.jpg') # Must be a single quote JSON otherwise doesn't work in frontend item.image_urls = json.dumps([url_for('main.upload_file', filename=img.image_location) for img in item.listing_images]).replace('"', '"') + + +@bp.route('/payment_success/<int:id>') +def payment_success(booking_id): + return render_template('payment_success.html', booking_id=booking_id) + +@bp.route('/generate-receipt/<int:id>') +def generate_receipt(id): + booking = Bookings.search_booking(id) + listing = Listings.search_listing(booking.listing_id) + pdf = create_receipt(1, booking.seat_type, '2024-02-24', listing) + + return send_file(pdf, as_attachment=True, download_name='receipt.pdf') + +@bp.route('/generate-ticket/<int:id>') +def generate_ticket(id): + booking = Bookings.search_booking(id) + listing = Listings.search_listing(booking.listing_id) + + pdf = create_plane_ticket(booking.seat_num, booking.seat_type, booking.depart_date, listing, booking_id) + + return send_file(pdf, as_attachment=True, download_name='plane_ticket.pdf') diff --git a/app/main/utils.py b/app/main/utils.py index fcaaffff9734bcb7a5831c3d3b688b9f119b2884..e362fda8837a905314a1702232b279ce4818b685 100644 --- a/app/main/utils.py +++ b/app/main/utils.py @@ -3,6 +3,12 @@ from flask import current_app from datetime import time, datetime from datetime import datetime +from fpdf import FPDF +import qrcode +import barcode +from barcode.writer import ImageWriter +from io import BytesIO +from PIL import Image def allowed_image_files(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS'] @@ -42,3 +48,80 @@ def pretty_time(unformatted_time, to_12_hour=True): formatted_time = unformatted_time.strftime("%H:%M") return formatted_time + + +def create_receipt(seat_num, seat_type, depart_date, listing): + pdf = FPDF() + pdf.add_page() + pdf.set_font("Arial", size=12) + + pdf.cell(200, 10, txt="Receipt", ln=True, align='C') + + pdf.cell(200, 10, txt=f"Seat Number: {seat_num}", ln=True) + pdf.cell(200, 10, txt=f"Seat Type: {seat_type}", ln=True) + pdf.cell(200, 10, txt=f"Departure Date: {depart_date}", ln=True) + pdf.cell(200, 10, txt=f"Departure Location: {listing.depart_location}", ln=True) + pdf.cell(200, 10, txt=f"Destination Location: {listing.destination_location}", ln=True) + + if seat_type.lower() == 'economy': + cost = listing.economy_fair_cost + elif seat_type.lower() == 'business': + cost = listing.business_fair_cost + + pdf.cell(200, 10, txt=f"Total Cost: {cost}", ln=True) + + output = BytesIO() + pdf.output(output) + output.seek(0) + + return output + +def create_plane_ticket(seat_num, seat_type, depart_date, listing, booking_id): + for i in range(seat_num): + seat_number = f"{seat_num + 1 - i:02d}" + ticket_number = f"{booking_id}-{seat_number}" + + pdf = FPDF() + pdf.add_page() + pdf.set_font("Arial", size=12) + + pdf.cell(200, 10, txt="Plane Ticket", ln=True, align='C') + + pdf.cell(200, 10, txt=f"Seat Number: {seat_number}", ln=True) + pdf.cell(200, 10, txt=f"Seat Type: {seat_type}", ln=True) + pdf.cell(200, 10, txt=f"Departure Date: {depart_date}", ln=True) + pdf.cell(200, 10, txt=f"Departure Location: {listing.depart_location}", ln=True) + pdf.cell(200, 10, txt=f"Departure Time: {listing.depart_time}", ln=True) + pdf.cell(200, 10, txt=f"Destination Location: {listing.destination_location}", ln=True) + pdf.cell(200, 10, txt=f"Destination Time: {listing.destination_time}", ln=True) + pdf.cell(200, 10, txt=f"Transport Type: {listing.transport_type}", ln=True) + + qr_img = create_qr_code(ticket_number) + qr_img_path = f"qr_code_{ticket_number}.png" + qr_img.save(qr_img_path) + pdf.image(qr_img_path, x=10, y=120, w=50) + + barcode_img = create_barcode(ticket_number) + barcode_img_path = f"barcode_{ticket_number}.png" + barcode_img.save(barcode_img_path) + pdf.image(barcode_img_path, x=80, y=120, w=100) + + output = BytesIO() + pdf.output(output) + output.seek(0) + + return output + +def create_qr_code(data): + qr = qrcode.QRCode(version=1, box_size=10, border=5) + qr.add_data(data) + qr.make(fit=True) + img = qr.make_image(fill='black', back_color='white') + return img + +def create_barcode(data): + CODE128 = barcode.get_barcode_class('code128') + code = CODE128(data, writer=ImageWriter()) + buffer = BytesIO() + code.write(buffer) + return Image.open(buffer) \ No newline at end of file diff --git a/app/templates/bookings/payment_success.html b/app/templates/bookings/payment_success.html new file mode 100644 index 0000000000000000000000000000000000000000..65a99ea3eb9010aee138b4406afb02c653c4b0d4 --- /dev/null +++ b/app/templates/bookings/payment_success.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% block content %} + +<body> + <h1>Payment Successful!</h1> + <p>Your payment has been processed successfully.</p> + <p>Click the buttons below to download your receipt and plane ticket:</p> + <form action="{{ url_for('bookings.generate_receipt', id=id) }}" method="get"> + <button type="submit">Download Receipt</button> + </form> + <form action="{{ url_for('bookings.generate_ticket', id=id) }}" method="get"> + <button type="submit">Download Plane Ticket</button> + </form> +</body> +{% endblock %} diff --git a/requirements.txt b/requirements.txt index 9945e842e5a7f4f54a60ed3fea364d9b5dc048a3..f8a540d7b7b5aa0c6dafef7ae94021d436a69393 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,7 @@ debugpy flask-wtf flask-security flask-principal -setuptools \ No newline at end of file +setuptools +fpdf2 +qrcode[pil] +python-barcode \ No newline at end of file