From 5a97f992dad65f7448c81710f3820aa0b43a92fe Mon Sep 17 00:00:00 2001 From: Machiel Date: Fri, 16 Oct 2026 20:14:40 +0200 Subject: [PATCH] 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 --- README.md | 2 +- app.py | 43 +++++++++++++++++++++++++++++++++++++++- templates/user_form.html | 2 ++ templates/users.html | 6 +++++- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c4f03ba..f970448 100644 --- a/README.md +++ b/README.md @@ -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 berekening; bestaande productprijzen worden alleen bijgewerkt als je dat aanvinkt (met producthistorie). 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 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. diff --git a/app.py b/app.py index a374c0d..2fd5a7f 100644 --- a/app.py +++ b/app.py @@ -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', 'api_tectum_woningen', 'api_tectum_woning', 'api_tectum_versie'} 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'} @@ -918,6 +918,47 @@ def user_edit(uid): 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//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 @app.route('/') diff --git a/templates/user_form.html b/templates/user_form.html index d086686..d38b7a9 100644 --- a/templates/user_form.html +++ b/templates/user_form.html @@ -19,6 +19,8 @@
Annuleren + {% if u.id and u.id != g.user.id %}{% endif %}
+{% if u.id and u.id != g.user.id %}
{% endif %} {% endblock %} diff --git a/templates/users.html b/templates/users.html index 82d9d42..2f1a910 100644 --- a/templates/users.html +++ b/templates/users.html @@ -1,4 +1,5 @@ {% extends 'base.html' %} +{% from '_bulk.html' import bulk_head, bulk_cell, bulk_bar with context %} {% block title %}Gebruikers{% endblock %} {% block content %}
@@ -7,10 +8,11 @@
- + {{ bulk_head() }} {% for u in rows %} + {{ bulk_cell(u.id, u.name) }} @@ -22,4 +24,6 @@
NaamE-mailRolContactenStatus
NaamE-mailRolContactenStatus

Beheerders kunnen ook gebruikers en instellingen beheren. Klanten krijgen toegang via het klantportaal (op de pagina van het contact).

+{{ 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 %}