Gebruikers: massa-actie actief/inactief maken of verwijderen (jezelf wordt overgeslagen) en een knop Verwijderen op het gebruikersformulier; contacten, taken en activiteiten blijven zonder eigenaar, agenda, meldingen en mailboxkoppelingen gaan mee
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
0368b7c1da
commit
5a97f992da
@@ -108,7 +108,7 @@ Eenvoudige CRM in Python (Flask) met SQLite, in de Van Emous-huisstijl. Versie 0
|
|||||||
in `kosten/leveranciers.csv`). Ontbreekt het product, dan wordt het aangemaakt met de prijs per eenheid van de
|
in `kosten/leveranciers.csv`). Ontbreekt het product, dan wordt het aangemaakt met de prijs per eenheid van de
|
||||||
berekening; bestaande productprijzen worden alleen bijgewerkt als je dat aanvinkt (met producthistorie).
|
berekening; bestaande productprijzen worden alleen bijgewerkt als je dat aanvinkt (met producthistorie).
|
||||||
De regels staan in `PRODUCT_RULES` in `static/bestek.js`.
|
De regels staan in `PRODUCT_RULES` in `static/bestek.js`.
|
||||||
- **Gebruikers** met de rollen beheerder (ook gebruikers en instellingen) en medewerker.
|
- **Gebruikers** met de rollen beheerder (ook gebruikers en instellingen) en medewerker; massa-actie actief/inactief maken of verwijderen (jezelf wordt overgeslagen). Verwijderen laat contacten, taken en activiteiten zonder eigenaar staan; agenda, meldingen en mailboxkoppelingen gaan mee.
|
||||||
- **Klantportaal** (`/portal`): de klant krijgt een uitnodigingslink (kopiëren of mailen vanaf het
|
- **Klantportaal** (`/portal`): de klant krijgt een uitnodigingslink (kopiëren of mailen vanaf het
|
||||||
contact), stelt een wachtwoord in en kan dan offertes bekijken, accepteren of afwijzen, bestanden
|
contact), stelt een wachtwoord in en kan dan offertes bekijken, accepteren of afwijzen, bestanden
|
||||||
uploaden en berichten sturen. Een geaccepteerde offerte zet het contact op gewonnen/klant.
|
uploaden en berichten sturen. Een geaccepteerde offerte zet het contact op gewonnen/klant.
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ API_SKIP = {'api_key', 'redirect', '_gotcha', 'website_hp', 'g-recaptcha-respons
|
|||||||
PUBLIC = {'static', 'login', 'setup', 'api_leads', 'api_products', 'dav', 'dav_wellknown', 'unsubscribe',
|
PUBLIC = {'static', 'login', 'setup', 'api_leads', 'api_products', 'dav', 'dav_wellknown', 'unsubscribe',
|
||||||
'api_tectum_woningen', 'api_tectum_woning', 'api_tectum_versie'}
|
'api_tectum_woningen', 'api_tectum_woning', 'api_tectum_versie'}
|
||||||
PORTAL_PUBLIC = {'portal_login', 'portal_invite'}
|
PORTAL_PUBLIC = {'portal_login', 'portal_invite'}
|
||||||
ADMIN_ONLY = {'settings', 'users', 'user_edit', 'mailbox_edit', 'routines', 'routine_edit', 'routine_run',
|
ADMIN_ONLY = {'settings', 'users', 'user_edit', 'users_bulk', 'user_delete', 'mailbox_edit', 'routines', 'routine_edit', 'routine_run',
|
||||||
'routine_delete'}
|
'routine_delete'}
|
||||||
|
|
||||||
|
|
||||||
@@ -918,6 +918,47 @@ def user_edit(uid):
|
|||||||
return render_template('user_form.html', u=u or {'role': 'user', 'active': 1})
|
return render_template('user_form.html', u=u or {'role': 'user', 'active': 1})
|
||||||
|
|
||||||
|
|
||||||
|
def delete_users(ids):
|
||||||
|
"""Verwijdert gebruikers: hun contacten, taken en activiteiten blijven (zonder eigenaar), hun agenda,
|
||||||
|
meldingen en mailboxkoppelingen gaan mee (foreign keys)."""
|
||||||
|
db().execute(f'DELETE FROM users WHERE id IN {id_list(ids)}', ids)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/users/<int:uid>/delete')
|
||||||
|
def user_delete(uid):
|
||||||
|
u = get_or_404('users', uid)
|
||||||
|
if u['id'] == g.user['id']:
|
||||||
|
flash('Je kunt jezelf niet verwijderen.', 'error')
|
||||||
|
return redirect(url_for('user_edit', uid=uid))
|
||||||
|
delete_users([uid])
|
||||||
|
db().commit()
|
||||||
|
flash(f"Gebruiker {u['name']} verwijderd.")
|
||||||
|
return redirect(url_for('users'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.post('/users/bulk')
|
||||||
|
def users_bulk():
|
||||||
|
ids, action = bulk_ids(), request.form.get('action')
|
||||||
|
skipped = g.user['id'] in ids
|
||||||
|
ids = [i for i in ids if i != g.user['id']] # jezelf niet buitensluiten
|
||||||
|
if not ids:
|
||||||
|
flash('Je kunt jezelf niet inactief maken of verwijderen.' if skipped else
|
||||||
|
'Vink eerst een of meer gebruikers aan.', 'error')
|
||||||
|
return bulk_back(url_for('users'))
|
||||||
|
if action in ('activate', 'deactivate'):
|
||||||
|
db().execute(f'UPDATE users SET active=? WHERE id IN {id_list(ids)}', [int(action == 'activate'), *ids])
|
||||||
|
msg = f"{len(ids)} gebruiker(s) op {'actief' if action == 'activate' else 'inactief'} gezet."
|
||||||
|
elif action == 'delete':
|
||||||
|
delete_users(ids)
|
||||||
|
msg = f'{len(ids)} gebruiker(s) verwijderd.'
|
||||||
|
else:
|
||||||
|
flash('Kies een actie.', 'error')
|
||||||
|
return bulk_back(url_for('users'))
|
||||||
|
db().commit()
|
||||||
|
flash(msg + (' Jezelf heb ik overgeslagen.' if skipped else ''))
|
||||||
|
return bulk_back(url_for('users'))
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------- dashboard
|
# ---------------------------------------------------------------- dashboard
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button class="btn btn-primary">Opslaan</button>
|
<button class="btn btn-primary">Opslaan</button>
|
||||||
<a class="btn btn-outline-secondary" href="{{ url_for('users') }}">Annuleren</a>
|
<a class="btn btn-outline-secondary" href="{{ url_for('users') }}">Annuleren</a>
|
||||||
|
{% if u.id and u.id != g.user.id %}<button class="btn btn-outline-secondary ms-auto" form="delete-user" onclick='return confirm({{ (u.name ~ " verwijderen? Contacten, taken en activiteiten blijven zonder eigenaar staan; agenda en meldingen verdwijnen. Alleen inactief maken kan ook: haal het vinkje Actief weg.")|tojson }})'>Verwijderen</button>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div></form>
|
</div></form>
|
||||||
|
{% if u.id and u.id != g.user.id %}<form id="delete-user" method="post" action="{{ url_for('user_delete', uid=u.id) }}"></form>{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
{% from '_bulk.html' import bulk_head, bulk_cell, bulk_bar with context %}
|
||||||
{% block title %}Gebruikers{% endblock %}
|
{% block title %}Gebruikers{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="head">
|
<div class="head">
|
||||||
@@ -7,10 +8,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card"><div class="card-body">
|
<div class="card"><div class="card-body">
|
||||||
<div class="table-responsive"><table class="table table-hover table-stack">
|
<div class="table-responsive"><table class="table table-hover table-stack">
|
||||||
<thead><tr><th>Naam</th><th>E-mail</th><th>Rol</th><th class="num">Contacten</th><th>Status</th></tr></thead>
|
<thead><tr>{{ bulk_head() }}<th>Naam</th><th>E-mail</th><th>Rol</th><th class="num">Contacten</th><th>Status</th></tr></thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for u in rows %}
|
{% for u in rows %}
|
||||||
<tr class="link" onclick="location='{{ url_for('user_edit', uid=u.id) }}'">
|
<tr class="link" onclick="location='{{ url_for('user_edit', uid=u.id) }}'">
|
||||||
|
{{ bulk_cell(u.id, u.name) }}
|
||||||
<td class="stack-title"><a href="{{ url_for('user_edit', uid=u.id) }}">{{ u.name }}</a></td>
|
<td class="stack-title"><a href="{{ url_for('user_edit', uid=u.id) }}">{{ u.name }}</a></td>
|
||||||
<td data-label="E-mail">{{ u.email }}</td>
|
<td data-label="E-mail">{{ u.email }}</td>
|
||||||
<td data-label="Rol">{{ ROLE.get(u.role, u.role) }}</td>
|
<td data-label="Rol">{{ ROLE.get(u.role, u.role) }}</td>
|
||||||
@@ -22,4 +24,6 @@
|
|||||||
</table></div>
|
</table></div>
|
||||||
</div></div>
|
</div></div>
|
||||||
<p class="muted">Beheerders kunnen ook gebruikers en instellingen beheren. Klanten krijgen toegang via het klantportaal (op de pagina van het contact).</p>
|
<p class="muted">Beheerders kunnen ook gebruikers en instellingen beheren. Klanten krijgen toegang via het klantportaal (op de pagina van het contact).</p>
|
||||||
|
{{ bulk_bar(url_for('users_bulk'), [('activate', 'Actief maken'), ('deactivate', 'Inactief maken (kan niet meer inloggen)'),
|
||||||
|
('delete', 'Verwijderen', '{n} gebruiker(s) verwijderen? Hun contacten, taken en activiteiten blijven zonder eigenaar staan; hun agenda en meldingen verdwijnen. Dit kan niet ongedaan worden gemaakt.')]) }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user