From 4f52c4ea7874b3851857cfc94b2043d7007d2147 Mon Sep 17 00:00:00 2001 From: def Date: Sun, 8 Mar 2026 07:33:59 +0000 Subject: [PATCH] Add v0.0.6 service management --- VERSION | 2 +- backend/app.py | 115 ++++++++++++++++++++++++++++++----- backend/utils.py | 17 +++--- templates/services/list.html | 45 ++++++++++++++ templates/services/new.html | 93 ++++++++++++++++++++++++++++ 5 files changed, 246 insertions(+), 26 deletions(-) create mode 100644 templates/services/list.html create mode 100644 templates/services/new.html diff --git a/VERSION b/VERSION index bbdeab6..1750564 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.5 +0.0.6 diff --git a/backend/app.py b/backend/app.py index ddb6f5e..e380305 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,6 +1,6 @@ from flask import Flask, render_template, request, redirect from db import get_db_connection -from utils import generate_client_code +from utils import generate_client_code, generate_service_code app = Flask( __name__, @@ -12,29 +12,36 @@ app = Flask( def index(): return """

OTB Billing

-

Version 0.0.5

+

Version 0.0.6

Clients

+

Services

+

DB Test

""" +@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("/clients/new", methods=["GET","POST"]) +@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"] @@ -42,25 +49,21 @@ def new_client(): conn = get_db_connection() cursor = conn.cursor(dictionary=True) - - cursor.execute("SELECT MAX(id) as last_id FROM clients") + 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) + VALUES (%s, %s, %s, %s, %s) """, (client_code, company_name, contact_name, email, phone) ) - conn.commit() conn.close() @@ -68,6 +71,86 @@ def new_client(): return render_template("clients/new.html") +@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("/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) diff --git a/backend/utils.py b/backend/utils.py index e432bbc..6ba7593 100644 --- a/backend/utils.py +++ b/backend/utils.py @@ -1,14 +1,13 @@ import re def generate_client_code(company_name, last_number): - # remove non letters - cleaned = re.sub(r'[^A-Za-z]', '', company_name.upper()) - - # first 5 characters - suffix = cleaned[:5] - + cleaned = re.sub(r'[^A-Za-z]', '', (company_name or '').upper()) + suffix = cleaned[:5] or "CLIENT" number = last_number + 1 + return f"{number:04d}-{suffix}" - code = f"{number:04d}-{suffix}" - - return code +def generate_service_code(service_name, last_number): + cleaned = re.sub(r'[^A-Za-z]', '', (service_name or '').upper()) + suffix = cleaned[:5] or "SERVI" + number = last_number + 1 + return f"SVC-{number:04d}-{suffix}" diff --git a/templates/services/list.html b/templates/services/list.html new file mode 100644 index 0000000..614cf84 --- /dev/null +++ b/templates/services/list.html @@ -0,0 +1,45 @@ + + + +Services + + + +

Services

+ +

Home

+

Add Service

+ + + + + + + + + + + + + + + +{% for s in services %} + + + + + + + + + + + + +{% endfor %} + +
IDService CodeClientService NameTypeCycleCurrencyAmountStatusStart Date
{{ s.id }}{{ s.service_code }}{{ s.client_code }} - {{ s.company_name }}{{ s.service_name }}{{ s.service_type }}{{ s.billing_cycle }}{{ s.currency_code }}{{ s.recurring_amount }}{{ s.status }}{{ s.start_date }}
+ + + diff --git a/templates/services/new.html b/templates/services/new.html new file mode 100644 index 0000000..7a8f19c --- /dev/null +++ b/templates/services/new.html @@ -0,0 +1,93 @@ + + + +New Service + + + +

Add Service

+ +
+ +

+Client
+ +

+ +

+Service Name
+ +

+ +

+Service Type
+ +

+ +

+Billing Cycle
+ +

+ +

+Currency Code
+ +

+ +

+Recurring Amount
+ +

+ +

+Status
+ +

+ +

+Start Date
+ +

+ +

+Description
+ +

+ +

+ +

+ +
+ + +