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>
45 lines
2.5 KiB
HTML
45 lines
2.5 KiB
HTML
{% extends 'base.html' %}
|
|
{% block title %}Producten{% endblock %}
|
|
{% block content %}
|
|
<div class="head">
|
|
<div><div class="kicker">Assortiment</div><h1>Producten</h1></div>
|
|
<div class="actions">
|
|
<form class="search" method="get">
|
|
<input type="hidden" name="show" value="{{ show }}">
|
|
<input type="search" name="q" value="{{ term }}" placeholder="Zoek op naam, SKU, leverancier…" aria-label="Zoeken">
|
|
</form>
|
|
<a class="btn ghost" href="{{ url_for('products_export') }}">Exporteer CSV</a>
|
|
<a class="btn ghost" href="{{ url_for('import_start', kind='products') }}">Importeren</a>
|
|
<a class="btn" href="{{ url_for('product_edit') }}">Nieuw product</a>
|
|
</div>
|
|
</div>
|
|
<div class="filters">
|
|
{% for k, l in [('active', 'Actief'), ('inactive', 'Inactief'), ('all', 'Alle')] %}
|
|
<a href="{{ url_for('products', show=k, q=term or None) }}" class="{{ 'on' if show == k }}">{{ l }}</a>
|
|
{% endfor %}
|
|
<span class="muted">{{ rows|length }} product(en)</span>
|
|
</div>
|
|
<div class="card">
|
|
{% if rows %}
|
|
<div class="table-wrap"><table>
|
|
<thead><tr><th></th><th>Naam</th><th>SKU</th><th>Leverancier</th><th>Eenheid</th><th class="num">Prijs excl.</th><th class="num">Btw</th><th>Bijgewerkt</th><th>Status</th></tr></thead>
|
|
<tbody>
|
|
{% for p in rows %}
|
|
<tr class="link" onclick="location='{{ url_for('product_edit', pid=p.id) }}'">
|
|
<td style="width:64px">{% if p.image %}<img class="thumb-sm" src="{{ url_for('upload_file', name=p.image) }}" alt="">{% else %}<span class="thumb-sm"></span>{% endif %}</td>
|
|
<td><a href="{{ url_for('product_edit', pid=p.id) }}">{{ p.name }}</a><div class="muted">{{ p.description|truncate(90) }}</div></td>
|
|
<td><code>{{ p.sku }}</code></td>
|
|
<td>{% if p.supplier_id %}<a href="{{ url_for('relation_view', rid=p.supplier_id) }}" onclick="event.stopPropagation()">{{ p.supplier }}</a>{% else %}{{ p.supplier }}{% endif %}</td>
|
|
<td>{{ p.unit }}</td>
|
|
<td class="num">{{ p.price|euro }}</td>
|
|
<td class="num">{{ p.vat|nr }}%</td>
|
|
<td class="muted">{{ (p.updated_at or p.created_at)|dt('%d-%m-%Y') }}</td>
|
|
<td>{% if not p.active %}<span class="chip verloren">Inactief</span>{% endif %}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table></div>
|
|
{% else %}<p class="empty">{{ 'Geen producten gevonden.' if term or show != 'active' else 'Nog geen producten. Voeg ze toe of importeer een CSV; daarna kies je ze in offertes.' }}</p>{% endif %}
|
|
</div>
|
|
{% endblock %}
|