billing frontend for mariadb. setup as otb_billing for outsidethebox.top accounting. also involved with outsidethedb
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

155 lines
6.0 KiB

<!doctype html>
<html>
<head>
<title>Edit Client</title>
<link rel="icon" type="image/png" href="/static/favicon.png">
</head>
<body>
<h1>Edit Client</h1>
<p><a href="/">Home</a></p>
<p><a href="/clients">Back to Clients</a></p>
<p>
<a href="/credits/{{ client.id }}"
style="{% if client.credit_balance > 0 %}color: green;{% elif client.credit_balance < 0 %}color: red;{% else %}color: blue;{% endif %}">
Ledger ({{ client.credit_balance|money('CAD') }})
</a>
</p>
{% 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">
<p>
Client Code<br>
<input value="{{ client.client_code }}" readonly>
</p>
<p>
Company Name *<br>
<input name="company_name" value="{{ client.company_name }}" required>
</p>
<p>
Contact Name<br>
<input name="contact_name" value="{{ client.contact_name or '' }}">
</p>
<p>
Email<br>
<input name="email" value="{{ client.email or '' }}">
</p>
<p>
Phone<br>
<input name="phone" value="{{ client.phone or '' }}">
</p>
<p>
Status *<br>
<select name="status" required>
<option value="lead" {% if client.status == 'lead' %}selected{% endif %}>lead</option>
<option value="active" {% if client.status == 'active' %}selected{% endif %}>active</option>
<option value="inactive" {% if client.status == 'inactive' %}selected{% endif %}>inactive</option>
<option value="suspended" {% if client.status == 'suspended' %}selected{% endif %}>suspended</option>
</select>
</p>
<p>
Notes<br>
<textarea name="notes" rows="5" cols="60">{{ client.notes or '' }}</textarea>
</p>
<p>
<button type="submit">Save Client</button>
</p>
</form>
<div style="margin-top:1.5rem;padding:1rem;border:1px solid rgba(255,255,255,0.16);border-radius:12px;background:rgba(255,255,255,0.03);">
<h3 style="margin-top:0;">Portal Access</h3>
<div style="margin-bottom:1rem;line-height:1.6;">
<div><strong>Portal Enabled:</strong> {{ "Yes" if client.portal_enabled else "No" }}</div>
<div><strong>Current Access Code:</strong> {{ client.portal_access_code or "Not set" }}</div>
<div><strong>Password Set At:</strong> {{ client.portal_password_set_at or "Not set" }}</div>
<div><strong>Access Code Created At:</strong> {{ client.portal_access_code_created_at or "Not set" }}</div>
<div><strong>Last Portal Login:</strong> {{ client.portal_last_login_at or "Never" }}</div>
</div>
{% if request.args.get("portal_reset_status") == "sent" %}
<div style="margin-top:0.75rem;margin-bottom:0.75rem;padding:0.85rem 1rem;border-radius:10px;border:1px solid rgba(80,200,120,0.35);background:rgba(80,200,120,0.10);color:#d8ffe2;">
Portal password reset email sent successfully.
</div>
{% elif request.args.get("portal_reset_status") == "missing_email" %}
<div style="margin-top:0.75rem;margin-bottom:0.75rem;padding:0.85rem 1rem;border-radius:10px;border:1px solid rgba(255,180,80,0.35);background:rgba(255,180,80,0.10);color:#ffe7c2;">
Portal password reset email was not sent because this client does not have an email address on file.
</div>
{% elif request.args.get("portal_reset_status") == "error" %}
<div style="margin-top:0.75rem;margin-bottom:0.75rem;padding:0.85rem 1rem;border-radius:10px;border:1px solid rgba(255,100,100,0.35);background:rgba(255,100,100,0.10);color:#ffd1d1;">
Portal password reset email could not be sent. Check SMTP settings and server logs.
</div>
{% endif %}
{% if request.args.get("portal_email_status") == "sent" %}
<div style="margin-top:0.75rem;margin-bottom:0.75rem;padding:0.85rem 1rem;border-radius:10px;border:1px solid rgba(80,200,120,0.35);background:rgba(80,200,120,0.10);color:#d8ffe2;">
Portal invite email sent successfully.
</div>
{% elif request.args.get("portal_email_status") == "missing_email" %}
<div style="margin-top:0.75rem;margin-bottom:0.75rem;padding:0.85rem 1rem;border-radius:10px;border:1px solid rgba(255,180,80,0.35);background:rgba(255,180,80,0.10);color:#ffe7c2;">
Portal invite email was not sent because this client does not have an email address on file.
</div>
{% elif request.args.get("portal_email_status") == "error" %}
<div style="margin-top:0.75rem;margin-bottom:0.75rem;padding:0.85rem 1rem;border-radius:10px;border:1px solid rgba(255,100,100,0.35);background:rgba(255,100,100,0.10);color:#ffd1d1;">
Portal invite email could not be sent. Check SMTP settings and server logs.
</div>
{% endif %}
<div style="display:flex;gap:0.75rem;flex-wrap:wrap;margin-bottom:0.75rem;">
{% if client.portal_enabled %}
<form method="post" action="/clients/portal/disable/{{ client.id }}" style="margin:0;">
<button type="submit">Disable Portal</button>
</form>
{% else %}
<form method="post" action="/clients/portal/enable/{{ client.id }}" style="margin:0;">
<button type="submit">Enable Portal</button>
</form>
{% endif %}
<form method="post" action="/clients/portal/reset-code/{{ client.id }}" style="margin:0;">
<button type="submit">Generate / Reset Access Code</button>
</form>
<form method="post" action="/clients/portal/send-invite/{{ client.id }}" style="margin:0;">
<button type="submit">Send Portal Invite Email</button>
</form>
<form method="post" action="/clients/portal/send-password-reset/{{ client.id }}" style="margin:0;">
<button type="submit">Send Password Reset Email</button>
</form>
</div>
<div style="font-size:0.95rem;opacity:0.9;">
Resetting the access code disables the current portal password and forces the client to set a new password on next login.
The portal access code is intended as a one-time access token and is cleared after successful password setup.
</div>
</div>
{% include "footer.html" %}
</body>
</html>