Browse Source

Add required field validation for invoice creation

main
def 2 weeks ago
parent
commit
c60c3538f6
  1. 2
      VERSION
  2. 95
      backend/app.py
  3. 59
      templates/invoices/new.html

2
VERSION

@ -1 +1 @@
0.0.7 0.0.8

95
backend/app.py

@ -12,7 +12,7 @@ app = Flask(
def index(): def index():
return """ return """
<h1>OTB Billing</h1> <h1>OTB Billing</h1>
<p>Version 0.0.7</p> <p>Version 0.0.8</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="/services">Services</a></p>
<p><a href="/invoices">Invoices</a></p> <p><a href="/invoices">Invoices</a></p>
@ -44,25 +44,20 @@ def clients():
def services(): def services():
conn = get_db_connection() conn = get_db_connection()
cursor = conn.cursor(dictionary=True) cursor = conn.cursor(dictionary=True)
cursor.execute(""" cursor.execute("""
SELECT s.*, c.client_code, c.company_name SELECT s.*, c.client_code, c.company_name
FROM services s FROM services s
JOIN clients c ON s.client_id = c.id JOIN clients c ON s.client_id = c.id
ORDER BY s.id DESC ORDER BY s.id DESC
""") """)
services = cursor.fetchall() services = cursor.fetchall()
conn.close() conn.close()
return render_template("services/list.html", services=services) return render_template("services/list.html", services=services)
@app.route("/invoices") @app.route("/invoices")
def invoices(): def invoices():
conn = get_db_connection() conn = get_db_connection()
cursor = conn.cursor(dictionary=True) cursor = conn.cursor(dictionary=True)
cursor.execute(""" cursor.execute("""
SELECT SELECT
i.*, i.*,
@ -72,35 +67,85 @@ def invoices():
JOIN clients c ON i.client_id = c.id JOIN clients c ON i.client_id = c.id
ORDER BY i.id DESC ORDER BY i.id DESC
""") """)
invoices = cursor.fetchall() invoices = cursor.fetchall()
conn.close() conn.close()
return render_template("invoices/list.html", invoices=invoices) return render_template("invoices/list.html", invoices=invoices)
@app.route("/invoices/new", methods=["GET","POST"]) @app.route("/invoices/new", methods=["GET", "POST"])
def new_invoice(): def new_invoice():
conn = get_db_connection() conn = get_db_connection()
cursor = conn.cursor(dictionary=True) cursor = conn.cursor(dictionary=True)
if request.method == "POST": 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()
client_id = request.form["client_id"] cursor.execute("""
service_id = request.form["service_id"] SELECT id, service_code, service_name
currency_code = request.form["currency_code"] FROM services
total_amount = request.form["total_amount"] ORDER BY service_name
due_at = request.form["due_at"] """)
notes = request.form["notes"] services = cursor.fetchall()
cursor.execute("SELECT MAX(id) as last_id FROM invoices") 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() result = cursor.fetchone()
number = (result["last_id"] or 0) + 1 number = (result["last_id"] or 0) + 1
invoice_number = f"INV-{number:04d}" invoice_number = f"INV-{number:04d}"
insert = conn.cursor() insert = conn.cursor()
insert.execute(""" insert.execute("""
INSERT INTO invoices INSERT INTO invoices
( (
@ -115,9 +160,8 @@ def new_invoice():
status, status,
notes notes
) )
VALUES (%s,%s,%s,%s,%s,%s,NOW(),%s,'pending',%s) VALUES (%s, %s, %s, %s, %s, %s, NOW(), %s, 'pending', %s)
""", """, (
(
client_id, client_id,
service_id, service_id,
invoice_number, invoice_number,
@ -149,8 +193,13 @@ def new_invoice():
conn.close() conn.close()
return render_template("invoices/new.html", clients=clients, services=services) return render_template(
"invoices/new.html",
clients=clients,
services=services,
errors=[],
form_data={},
)
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0", port=5050) app.run(host="0.0.0.0", port=5050)

59
templates/invoices/new.html

@ -8,49 +8,66 @@
<h1>Create Invoice</h1> <h1>Create Invoice</h1>
{% if errors %}
<div style="border:1px solid red; padding:10px; margin-bottom:15px;">
<strong>Please fix the following:</strong>
<ul>
{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
<form method="post"> <form method="post">
<p> <p>
Client<br> Client *<br>
<select name="client_id"> <select name="client_id" required>
{% for c in clients %} <option value="">Select client</option>
<option value="{{ c.id }}">{{ c.client_code }} - {{ c.company_name }}</option> {% for c in clients %}
{% endfor %} <option value="{{ c.id }}" {% if form_data.get('client_id') == (c.id|string) %}selected{% endif %}>
{{ c.client_code }} - {{ c.company_name }}
</option>
{% endfor %}
</select> </select>
</p> </p>
<p> <p>
Service<br> Service *<br>
<select name="service_id"> <select name="service_id" required>
{% for s in services %} <option value="">Select service</option>
<option value="{{ s.id }}">{{ s.service_code }} - {{ s.service_name }}</option> {% for s in services %}
{% endfor %} <option value="{{ s.id }}" {% if form_data.get('service_id') == (s.id|string) %}selected{% endif %}>
{{ s.service_code }} - {{ s.service_name }}
</option>
{% endfor %}
</select> </select>
</p> </p>
<p> <p>
Currency<br> Currency *<br>
<select name="currency_code"> <select name="currency_code" required>
<option value="CAD">CAD</option> <option value="CAD" {% if form_data.get('currency_code', 'CAD') == 'CAD' %}selected{% endif %}>CAD</option>
<option value="ETHO">ETHO</option> <option value="ETHO" {% if form_data.get('currency_code') == 'ETHO' %}selected{% endif %}>ETHO</option>
<option value="EGAZ">EGAZ</option> <option value="EGAZ" {% if form_data.get('currency_code') == 'EGAZ' %}selected{% endif %}>EGAZ</option>
<option value="ALT">ALT</option> <option value="ALT" {% if form_data.get('currency_code') == 'ALT' %}selected{% endif %}>ALT</option>
</select> </select>
</p> </p>
<p> <p>
Total Amount<br> Total Amount *<br>
<input type="number" step="0.00000001" name="total_amount"> <input type="number" step="0.00000001" min="0.00000001" name="total_amount" value="{{ form_data.get('total_amount', '') }}" required>
</p> </p>
<p> <p>
Due Date<br> Due Date *<br>
<input type="date" name="due_at"> <input type="date" name="due_at" value="{{ form_data.get('due_at', '') }}" required>
</p> </p>
<p> <p>
Notes<br> Notes<br>
<textarea name="notes"></textarea> <textarea name="notes">{{ form_data.get('notes', '') }}</textarea>
</p> </p>
<p> <p>

Loading…
Cancel
Save