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"
{result[0]}
" except Exception as e: return f"{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)