from flask import Flask, render_template, request, redirect from db import get_db_connection from utils import generate_client_code, generate_service_code app = Flask( __name__, template_folder="../templates", static_folder="../static", ) @app.route("/") def index(): conn = get_db_connection() cursor = conn.cursor(dictionary=True) cursor.execute("SELECT COUNT(*) AS total_clients FROM clients") total_clients = cursor.fetchone()["total_clients"] cursor.execute("SELECT COUNT(*) AS active_services FROM services WHERE status = 'active'") active_services = cursor.fetchone()["active_services"] cursor.execute(""" SELECT COUNT(*) AS outstanding_invoices FROM invoices WHERE status IN ('pending', 'partial', 'overdue') """) outstanding_invoices = cursor.fetchone()["outstanding_invoices"] cursor.execute(""" SELECT COALESCE(SUM(payment_amount), 0) AS revenue_received FROM payments WHERE payment_status = 'confirmed' AND payment_currency = 'CAD' """) revenue_received = cursor.fetchone()["revenue_received"] conn.close() return render_template( "dashboard.html", total_clients=total_clients, active_services=active_services, outstanding_invoices=outstanding_invoices, revenue_received=revenue_received, ) @app.route("/dbtest") def dbtest(): try: conn = get_db_connection() cursor = conn.cursor() cursor.execute("SELECT NOW()") result = cursor.fetchone() conn.close() return f"

Database OK

{result[0]}

" except Exception as e: return f"

Database FAILED

{e}
" @app.route("/clients") def clients(): conn = get_db_connection() cursor = conn.cursor(dictionary=True) cursor.execute("SELECT * FROM clients ORDER BY id DESC") clients = cursor.fetchall() conn.close() return render_template("clients/list.html", clients=clients) @app.route("/services") def services(): conn = get_db_connection() cursor = conn.cursor(dictionary=True) cursor.execute(""" SELECT s.*, c.client_code, c.company_name FROM services s JOIN clients c ON s.client_id = c.id ORDER BY s.id DESC """) services = cursor.fetchall() conn.close() return render_template("services/list.html", services=services) @app.route("/invoices") def invoices(): conn = get_db_connection() cursor = conn.cursor(dictionary=True) cursor.execute(""" SELECT i.*, c.client_code, c.company_name FROM invoices i JOIN clients c ON i.client_id = c.id ORDER BY i.id DESC """) invoices = cursor.fetchall() conn.close() return render_template("invoices/list.html", invoices=invoices) @app.route("/invoices/new", methods=["GET", "POST"]) def new_invoice(): conn = get_db_connection() cursor = conn.cursor(dictionary=True) if request.method == "POST": client_id = request.form.get("client_id", "").strip() service_id = request.form.get("service_id", "").strip() currency_code = request.form.get("currency_code", "").strip() total_amount = request.form.get("total_amount", "").strip() due_at = request.form.get("due_at", "").strip() notes = request.form.get("notes", "").strip() errors = [] if not client_id: errors.append("Client is required.") if not service_id: errors.append("Service is required.") if not currency_code: errors.append("Currency is required.") if not total_amount: errors.append("Total amount is required.") if not due_at: errors.append("Due date is required.") if not errors: try: amount_value = float(total_amount) if amount_value <= 0: errors.append("Total amount must be greater than zero.") except ValueError: errors.append("Total amount must be a valid number.") if errors: cursor.execute(""" SELECT id, client_code, company_name FROM clients ORDER BY company_name """) clients = cursor.fetchall() cursor.execute(""" SELECT id, service_code, service_name FROM services ORDER BY service_name """) services = cursor.fetchall() conn.close() form_data = { "client_id": client_id, "service_id": service_id, "currency_code": currency_code, "total_amount": total_amount, "due_at": due_at, "notes": notes, } return render_template( "invoices/new.html", clients=clients, services=services, errors=errors, form_data=form_data, ) cursor.execute("SELECT MAX(id) AS last_id FROM invoices") result = cursor.fetchone() number = (result["last_id"] or 0) + 1 invoice_number = f"INV-{number:04d}" insert = conn.cursor() insert.execute(""" INSERT INTO invoices ( client_id, service_id, invoice_number, currency_code, total_amount, subtotal_amount, issued_at, due_at, status, notes ) VALUES (%s, %s, %s, %s, %s, %s, NOW(), %s, 'pending', %s) """, ( client_id, service_id, invoice_number, currency_code, total_amount, total_amount, due_at, notes )) conn.commit() conn.close() return redirect("/invoices") cursor.execute(""" SELECT id, client_code, company_name FROM clients ORDER BY company_name """) clients = cursor.fetchall() cursor.execute(""" SELECT id, service_code, service_name FROM services ORDER BY service_name """) services = cursor.fetchall() conn.close() return render_template( "invoices/new.html", clients=clients, services=services, errors=[], form_data={}, ) @app.route("/clients/new", methods=["GET", "POST"]) def new_client(): if request.method == "POST": company_name = request.form["company_name"] contact_name = request.form["contact_name"] email = request.form["email"] phone = request.form["phone"] conn = get_db_connection() cursor = conn.cursor(dictionary=True) cursor.execute("SELECT MAX(id) AS last_id FROM clients") result = cursor.fetchone() last_number = result["last_id"] if result["last_id"] else 0 client_code = generate_client_code(company_name, last_number) cursor = conn.cursor() cursor.execute( """ INSERT INTO clients (client_code, company_name, contact_name, email, phone) VALUES (%s, %s, %s, %s, %s) """, (client_code, company_name, contact_name, email, phone) ) conn.commit() conn.close() return redirect("/clients") return render_template("clients/new.html") @app.route("/services/new", methods=["GET", "POST"]) def new_service(): conn = get_db_connection() cursor = conn.cursor(dictionary=True) if request.method == "POST": client_id = request.form["client_id"] service_name = request.form["service_name"] service_type = request.form["service_type"] billing_cycle = request.form["billing_cycle"] currency_code = request.form["currency_code"] recurring_amount = request.form["recurring_amount"] status = request.form["status"] start_date = request.form["start_date"] or None description = request.form["description"] cursor.execute("SELECT MAX(id) AS last_id FROM services") result = cursor.fetchone() last_number = result["last_id"] if result["last_id"] else 0 service_code = generate_service_code(service_name, last_number) insert_cursor = conn.cursor() insert_cursor.execute( """ INSERT INTO services ( client_id, service_code, service_name, service_type, billing_cycle, status, currency_code, recurring_amount, start_date, description ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """, ( client_id, service_code, service_name, service_type, billing_cycle, status, currency_code, recurring_amount, start_date, description ) ) conn.commit() conn.close() return redirect("/services") cursor.execute("SELECT id, client_code, company_name FROM clients ORDER BY company_name ASC") clients = cursor.fetchall() conn.close() return render_template("services/new.html", clients=clients) if __name__ == "__main__": app.run(host="0.0.0.0", port=5050)