v0.0.1: eerste versie van de Van Emous CRM

Flask + SQLite, in de Van Emous-huisstijl (tokens uit het design system, GSAP-effecten
volgens de motion-regels, uit bij prefers-reduced-motion).

- Leads en contactpersonen: status, toewijzen, verwachte waarde, omzetten naar klant,
  tijdlijn, bestanden, taken/opvolging
- Relaties (zoals accounts in EspoCRM): klantbundels (huishouden/familie) en partners met
  type (leverancier, aannemer, ...); leveranciers van producten worden automatisch partners
- API voor formulieren (POST /api/leads) met dubbele-leaddetectie, bestanden en
  configurator-ontwerpen (house-design JSON)
- Import van CSV/JSON (leads, relaties, producten) met kolomkoppeling; configurator-formaat
- Producten met SKU als vaste sleutel, mutatiehistorie, POST /api/products, CSV-export;
  offerteregels houden hun eigen prijs
- Offertes met btw per tarief en afdrukversie (PDF via de browser)
- Inbox via IMAP/SMTP, gekoppeld aan contacten
- Gebruikers (beheerder/medewerker) en klantportaal (offertes accepteren, bestanden, berichten)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-10-14 21:56:55 +02:00
co-authored by Claude Opus 5.5
commit bffa053c05
33 changed files with 4111 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
{% extends 'base.html' %}
{% block title %}{{ 'Nieuw contact' if new else c.name }}{% endblock %}
{% block content %}
<div class="head">
<div>
<a class="label" href="{{ url_for('contacts') if new else url_for('contact_view', cid=c.id) }}">← Terug</a>
<h1>{{ ('Nieuwe ' ~ TYPE.get(c.type, 'lead')|lower) if new else 'Contact bewerken' }}</h1>
</div>
</div>
<form method="post" class="card">
<div class="fields">
<div><label for="name">Naam *</label><input id="name" name="name" value="{{ c.name }}" required></div>
<div><label for="company">Bedrijf</label><input id="company" name="company" value="{{ c.company }}"></div>
<div><label for="email">E-mail</label><input id="email" type="email" name="email" value="{{ c.email }}"></div>
<div><label for="phone">Telefoon</label><input id="phone" name="phone" value="{{ c.phone }}"></div>
<div class="full"><label for="address">Adres</label><input id="address" name="address" value="{{ c.address }}"></div>
<div><label for="postcode">Postcode</label><input id="postcode" name="postcode" value="{{ c.postcode }}"></div>
<div><label for="city">Plaats</label><input id="city" name="city" value="{{ c.city }}"></div>
<div><label for="status">Status</label>
<select id="status" name="status">
{% for k, l in STATUSES %}<option value="{{ k }}" {{ 'selected' if c.status == k }}>{{ l }}</option>{% endfor %}
</select>
</div>
<div><label for="type">Soort</label>
<select id="type" name="type">
{% for k, l in TYPES %}<option value="{{ k }}" {{ 'selected' if c.type == k }}>{{ l }}</option>{% endfor %}
</select>
</div>
<div><label for="assigned">Toegewezen aan</label>
<select id="assigned" name="assigned_user_id">
<option value="">— Niemand —</option>
{% for u in USERS %}<option value="{{ u.id }}" {{ 'selected' if c.assigned_user_id|string == u.id|string }}>{{ u.name }}</option>{% endfor %}
</select>
</div>
<div><label for="relation">Relatie</label>
<select id="relation" name="relation_id">
<option value="">— Geen —</option>
{% for kind, label in RELATION_KINDS %}
<optgroup label="{{ label }}s">
{% for r in relations if r.kind == kind %}<option value="{{ r.id }}" {{ 'selected' if c.relation_id|string == r.id|string }}>{{ r.name }}{% if r.type %} · {{ r.type }}{% endif %}</option>{% endfor %}
</optgroup>
{% endfor %}
</select>
<p class="hint">Huishouden, familie of bedrijf van de klant, of de partner waar deze persoon werkt.</p>
</div>
<div><label for="role">Rol binnen relatie</label><input id="role" name="relation_role" value="{{ c.relation_role }}" placeholder="Bijv. bewoner, mantelzorger, inkoper"></div>
<div><label for="value">Verwachte waarde (€)</label><input id="value" type="number" step="0.01" name="value" value="{{ c.value or '' }}"></div>
<div><label for="source">Bron</label><input id="source" name="source" value="{{ c.source }}" placeholder="Bijv. website, beurs, telefoon"></div>
<div class="full"><label for="notes">Vaste notities</label><textarea id="notes" name="notes">{{ c.notes }}</textarea></div>
</div>
<div class="actions">
<button class="btn">Opslaan</button>
<a class="btn quiet" href="{{ url_for('contacts') if new else url_for('contact_view', cid=c.id) }}">Annuleren</a>
</div>
</form>
{% endblock %}