Browse Source

Add v0.0.6 service management

main
def 2 weeks ago
parent
commit
4f52c4ea78
  1. 2
      VERSION
  2. 111
      backend/app.py
  3. 17
      backend/utils.py
  4. 45
      templates/services/list.html
  5. 93
      templates/services/new.html

2
VERSION

@ -1 +1 @@
0.0.5 0.0.6

111
backend/app.py

@ -1,6 +1,6 @@
from flask import Flask, render_template, request, redirect from flask import Flask, render_template, request, redirect
from db import get_db_connection from db import get_db_connection
from utils import generate_client_code from utils import generate_client_code, generate_service_code
app = Flask( app = Flask(
__name__, __name__,
@ -12,29 +12,36 @@ app = Flask(
def index(): def index():
return """ return """
<h1>OTB Billing</h1> <h1>OTB Billing</h1>
<p>Version 0.0.5</p> <p>Version 0.0.6</p>
<p><a href="/clients">Clients</a></p> <p><a href="/clients">Clients</a></p>
<p><a href="/services">Services</a></p>
<p><a href="/dbtest">DB Test</a></p>
""" """
@app.route("/dbtest")
def dbtest():
try:
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute("SELECT NOW()")
result = cursor.fetchone()
conn.close()
return f"<h1>Database OK</h1><p>{result[0]}</p>"
except Exception as e:
return f"<h1>Database FAILED</h1><pre>{e}</pre>"
@app.route("/clients") @app.route("/clients")
def clients(): def clients():
conn = get_db_connection() conn = get_db_connection()
cursor = conn.cursor(dictionary=True) cursor = conn.cursor(dictionary=True)
cursor.execute("SELECT * FROM clients ORDER BY id DESC") cursor.execute("SELECT * FROM clients ORDER BY id DESC")
clients = cursor.fetchall() clients = cursor.fetchall()
conn.close() conn.close()
return render_template("clients/list.html", clients=clients) 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(): def new_client():
if request.method == "POST": if request.method == "POST":
company_name = request.form["company_name"] company_name = request.form["company_name"]
contact_name = request.form["contact_name"] contact_name = request.form["contact_name"]
email = request.form["email"] email = request.form["email"]
@ -42,16 +49,13 @@ def new_client():
conn = get_db_connection() conn = get_db_connection()
cursor = conn.cursor(dictionary=True) 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() result = cursor.fetchone()
last_number = result["last_id"] if result["last_id"] else 0 last_number = result["last_id"] if result["last_id"] else 0
client_code = generate_client_code(company_name, last_number) client_code = generate_client_code(company_name, last_number)
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute( cursor.execute(
""" """
INSERT INTO clients INSERT INTO clients
@ -60,7 +64,6 @@ def new_client():
""", """,
(client_code, company_name, contact_name, email, phone) (client_code, company_name, contact_name, email, phone)
) )
conn.commit() conn.commit()
conn.close() conn.close()
@ -68,6 +71,86 @@ def new_client():
return render_template("clients/new.html") 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__": if __name__ == "__main__":
app.run(host="0.0.0.0", port=5050) app.run(host="0.0.0.0", port=5050)

17
backend/utils.py

@ -1,14 +1,13 @@
import re import re
def generate_client_code(company_name, last_number): def generate_client_code(company_name, last_number):
# remove non letters cleaned = re.sub(r'[^A-Za-z]', '', (company_name or '').upper())
cleaned = re.sub(r'[^A-Za-z]', '', company_name.upper()) suffix = cleaned[:5] or "CLIENT"
# first 5 characters
suffix = cleaned[:5]
number = last_number + 1 number = last_number + 1
return f"{number:04d}-{suffix}"
code = f"{number:04d}-{suffix}" def generate_service_code(service_name, last_number):
cleaned = re.sub(r'[^A-Za-z]', '', (service_name or '').upper())
return code suffix = cleaned[:5] or "SERVI"
number = last_number + 1
return f"SVC-{number:04d}-{suffix}"

45
templates/services/list.html

@ -0,0 +1,45 @@
<!doctype html>
<html>
<head>
<title>Services</title>
</head>
<body>
<h1>Services</h1>
<p><a href="/">Home</a></p>
<p><a href="/services/new">Add Service</a></p>
<table border="1" cellpadding="6">
<tr>
<th>ID</th>
<th>Service Code</th>
<th>Client</th>
<th>Service Name</th>
<th>Type</th>
<th>Cycle</th>
<th>Currency</th>
<th>Amount</th>
<th>Status</th>
<th>Start Date</th>
</tr>
{% for s in services %}
<tr>
<td>{{ s.id }}</td>
<td>{{ s.service_code }}</td>
<td>{{ s.client_code }} - {{ s.company_name }}</td>
<td>{{ s.service_name }}</td>
<td>{{ s.service_type }}</td>
<td>{{ s.billing_cycle }}</td>
<td>{{ s.currency_code }}</td>
<td>{{ s.recurring_amount }}</td>
<td>{{ s.status }}</td>
<td>{{ s.start_date }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>

93
templates/services/new.html

@ -0,0 +1,93 @@
<!doctype html>
<html>
<head>
<title>New Service</title>
</head>
<body>
<h1>Add Service</h1>
<form method="post">
<p>
Client<br>
<select name="client_id" required>
<option value="">Select client</option>
{% for c in clients %}
<option value="{{ c.id }}">{{ c.client_code }} - {{ c.company_name }}</option>
{% endfor %}
</select>
</p>
<p>
Service Name<br>
<input name="service_name" required>
</p>
<p>
Service Type<br>
<select name="service_type" required>
<option value="hosting">hosting</option>
<option value="rpc">rpc</option>
<option value="explorer">explorer</option>
<option value="node">node</option>
<option value="ipfs">ipfs</option>
<option value="consulting">consulting</option>
<option value="other">other</option>
</select>
</p>
<p>
Billing Cycle<br>
<select name="billing_cycle" required>
<option value="one_time">one_time</option>
<option value="monthly" selected>monthly</option>
<option value="quarterly">quarterly</option>
<option value="yearly">yearly</option>
<option value="manual">manual</option>
</select>
</p>
<p>
Currency Code<br>
<select name="currency_code" required>
<option value="CAD" selected>CAD</option>
<option value="ETHO">ETHO</option>
<option value="EGAZ">EGAZ</option>
<option value="ALT">ALT</option>
</select>
</p>
<p>
Recurring Amount<br>
<input type="number" step="0.00000001" name="recurring_amount" value="0.00000000" required>
</p>
<p>
Status<br>
<select name="status" required>
<option value="pending">pending</option>
<option value="active" selected>active</option>
<option value="suspended">suspended</option>
<option value="cancelled">cancelled</option>
</select>
</p>
<p>
Start Date<br>
<input type="date" name="start_date">
</p>
<p>
Description<br>
<textarea name="description" rows="5" cols="60"></textarea>
</p>
<p>
<button type="submit">Create Service</button>
</p>
</form>
</body>
</html>
Loading…
Cancel
Save