{% extends "base.html" %} {% from "_macros.html" import money, sparkline_widget %} {% block title %}{{ customer.name }}{% endblock %} {% block body_data %}data-page-type="customer" data-page-id="{{ customer.id }}" data-page-label="{{ customer.name }}" data-page-sub="{{ customer.city or '' }} {{ customer.country_iso or '' }}"{% endblock %} {% block breadcrumb %}

Kunden / {{ customer.company_code or customer.name }}

{% endblock %} {% block heading %}{{ customer.name }}{% endblock %} {% block topbar_actions %} {% if not customer.is_business %}B2C {% elif is_eu_country(customer.country_iso) and customer.country_iso != 'DE' %}EU {% elif customer.country_iso and not is_eu_country(customer.country_iso) %}Drittland{% endif %} Stammdaten bearbeiten + Anfrage + Angebot + Auftrag {% endblock %} {% block content %} {# v1.12.0: Inline-Sparkline Umsatz 12 Monate #}
{{ sparkline_widget("/api/sparkline/customer/" ~ customer.id, "Umsatz letzte 12 Monate", "revenue", "€") }}
{# ---------- KPI tiles ---------- #}

Laufende Aufträge

{{ kpis.n_sales_orders_active }}

von {{ kpis.n_sales_orders }} gesamt

Offene Angebote

{{ kpis.n_quotes_open }}

{{ kpis.n_quotes }} insgesamt

Offene Anfragen

{{ kpis.n_inquiries }}

erfasst

Forderungen

{{ money(kpis.outstanding_value) }}

{% if kpis.overdue_value %}{{ money(kpis.overdue_value) }} überfällig{% else %}{{ kpis.outstanding_count }} offen{% endif %}

Umsatz YTD

{{ money(kpis.revenue_ytd) }}

{{ money(kpis.revenue_total) }} gesamt

Zahlungs­verhalten

{% if kpis.avg_payment_delay_days is not none %} {% set d = kpis.avg_payment_delay_days %}

{% if d > 0 %}+{% endif %}{{ "%.0f"|format(d) }}T

{% if d <= 0 %}pünktlich{% elif d < 7 %}leicht verzögert{% else %}im Mahnbereich{% endif %}

{% else %}

noch keine Daten

{% endif %}
{# ---------- LEFT: Belege (2/3) ---------- #}
{# Laufende Aufträge — der user wollte das prominent #}

Laufende Aufträge

Aktiv in Bearbeitung — nicht abgeschlossen

{% if active_orders %} {% for o in active_orders %} {% set prog = order_progress.get(o.id) or {} %} {% endfor %}
Auftrags-Nr. Datum Liefertermin Priorität Aktuelle Stufe Wert (brutto) Status
{{ o.order_no }} {{ o.order_date }} {{ o.due_date or '—' }} {% if o.priority and o.priority != 'normal' %} {{ PRIORITY_LABELS.get(o.priority, o.priority) }} {% else %}{% endif %} {% if prog.current_step %} {{ prog.current_step.step_name }} {% if prog.total %}{{ prog.completed }}/{{ prog.total }}{% endif %} {% else %} noch nicht gestartet {% endif %} {{ money(o.total_gross) }} {{ o.currency }} {{ SO_STATUS_LABELS.get(o.status, o.status) }}
{% else %}
Aktuell laufen keine Aufträge für diesen Kunden. + Neuen Auftrag anlegen
{% endif %}
{# Anfragen #} {% if inquiries %}

Anfragen · {{ inquiries|length }}

{% for i in inquiries %} {% endfor %}
Nr.EingangBetreffStatusAngebot
{{ i.inquiry_no }} {{ i.created_at[:10] }} {{ i.subject }} {{ INQ_STATUS_LABELS.get(i.status, i.status) }} {% if i.created_quote_id %}#{{ i.created_quote_id }}{% else %}—{% endif %}
{% endif %} {# Angebote #} {% if quotes %}

Angebote · {{ quotes|length }}

{% for q in quotes %} {% endfor %}
AngebotDatumGültig bisBruttoStatusAuftrag
{{ q.quote_no }} {{ q.quote_date }} {{ q.valid_until or '—' }} {{ money(q.total_gross) }} {{ QUOTE_STATUS_LABELS.get(q.status, q.status) }} {% if q.converted_sales_order_id %}→ #{{ q.converted_sales_order_id }}{% else %}—{% endif %}
{% endif %} {# Abgeschlossene Aufträge #} {% if closed_orders %}

Abgeschlossene Aufträge · {{ closed_orders|length }}

Bezahlt oder storniert

{% for o in closed_orders %} {% endfor %}
Auftrags-Nr.DatumBruttoStatus
{{ o.order_no }} {{ o.order_date }} {{ money(o.total_gross) }} {{ SO_STATUS_LABELS.get(o.status, o.status) }}
{% endif %} {# Offene Posten — pro Kunde #} {% if open_items and open_items.total_open > 0 %}

Offene Posten

{{ open_items.rows|length }} offene Rechnung(en) · gesamt-Forderung {{ '%.2f'|format(open_items.total_open) }} €

{% set b = open_items.buckets %} ≤ 0 T: {{ '%.2f'|format(b.current) }} {% if b.b30 > 0 %}1–30 T: {{ '%.2f'|format(b.b30) }}{% endif %} {% if b.b60 > 0 %}31–60 T: {{ '%.2f'|format(b.b60) }}{% endif %} {% if b.b90 > 0 %}61–90 T: {{ '%.2f'|format(b.b90) }}{% endif %} {% if b.over > 0 %}> 90 T: {{ '%.2f'|format(b.over) }}{% endif %}
{% for op in open_items.rows %} {% endfor %}
Rechnungs-Nr. Auftrag Fällig Tage über Offen Mahnstufe
{{ op.invoice_no }} {{ op.order_no or '—' }} {{ (op.due_date or '')[:10] or '—' }} {% if op.days_over > 0 %} +{{ op.days_over }} {% elif op.days_over < 0 %} {{ op.days_over }} {% else %} heute {% endif %} {{ '%.2f'|format(op.open_amount) }} {{ op.currency or '€' }} {% if op.dunning_level == 1 %}Mahnung 1 {% elif op.dunning_level == 2 %}Mahnung 2 {% elif op.dunning_level == 3 %}Letzte Mahnung {% else %}{% endif %}
{% endif %} {# v1.12.1: Kundenteile / Kunden-Lager #}

Bauteile & Kunden-Lager · {{ customer_parts|length }}

Endprodukte die du für diesen Kunden fertigst · Konsignations-/Kundenfertigung-Bestand

+ Neues Bauteil
{% if customer_parts %}
Teile
{{ stock_summary.part_count }}
Gesamt-Bestand
{{ stock_summary.total_qty }}
Unter Mindest
{{ stock_summary.below_min_count }}
{% for p in customer_parts %} {% set reserved = p.qty_reserved|int or 0 %} {% set in_prod = p.qty_in_production|int or 0 %} {% set stock = p.quantity_total or 0 %} {% set avail = stock - reserved + in_prod %} {% set deficit = (reserved - stock - in_prod) if (reserved - stock - in_prod) > 0 else 0 %} {% set below_min = p.quantity_min and (stock - reserved) < p.quantity_min %} {% set alert = deficit > 0 or below_min %} {% endfor %}
SKU Bezeichnung Kd.-Teile-Nr. Lager Reserviert In Prod. Verfügbar Min
{{ p.ident }} {{ p.description or '—' }} {{ p.customer_part_no or '—' }} {{ stock }} {% if reserved > 0 %}{{ reserved }}{% else %}—{% endif %} {% if in_prod > 0 %}{{ in_prod }}{% else %}—{% endif %} {{ avail }} {% if deficit > 0 %}
fehlen {{ deficit }}
{% endif %}
{{ p.quantity_min or '—' }} {% if alert %} {% else %} {% endif %}
{% else %}

Noch keine Bauteile für diesen Kunden angelegt.

Lege ein Bauteil als Kundenteil an — dann erscheint hier sein Lagerbestand.

+ Erstes Bauteil anlegen
{% endif %}
{# Rechnungen #} {% if invoices %}

Rechnungen · {{ invoices|length }}

{% for inv in invoices %} {% set open_amt = (inv.total_gross or 0) - (inv.paid_amount or 0) %} {% set overdue = inv.due_date and inv.due_date < '2026-04-29' and open_amt > 0 %} {% endfor %}
Rechnungs-Nr.DatumFälligBruttoOffenStatus
{{ inv.invoice_no }} {% if inv.kind == 'storno' %}STORNO{% endif %} {{ inv.issue_date }} {{ inv.due_date or '—' }} {{ money(inv.total_gross) }} {{ money(open_amt) }} {{ INV_STATUS_LABELS.get(inv.status, inv.status) }}
{% endif %}
{# ---------- RIGHT: Stammdaten (1/3) ---------- #}

Stammdaten

Kunden-Code

{{ customer.company_code or '—' }}

Adresse

{{ customer.name }}

{% if customer.address %}

{{ customer.address }}

{% endif %} {% if customer.address_line2 %}

{{ customer.address_line2 }}

{% endif %}

{{ customer.postcode }} {{ customer.city }}

{{ country_name_de(customer.country_iso) or customer.country }}

Steuer

USt-IdNr: {{ customer.vat_id or '—' }}

{% if customer.tax_id %}

Steuer-Nr: {{ customer.tax_id }}

{% endif %}

{% if customer.is_business %}B2B (Geschäftskunde){% else %}B2C (Privatkunde){% endif %}

Rechnungsstellung

Format: {{ customer.preferred_invoice_format }}

Zahlungsziel: {{ customer.payment_terms_days }} Tage

{% if customer.leitweg_id %}

Leitweg-ID: {{ customer.leitweg_id }}

{% endif %}

Hauptkontakt

{% if customer.contact_name %}

{{ customer.contact_name }}

{% endif %} {% if customer.email %}

{{ customer.email }}

{% endif %} {% if customer.phone %}

{{ customer.phone }}

{% endif %} {% if customer.website %}

{{ customer.website }}

{% endif %}
{% if contacts %}

Weitere Ansprechpartner ({{ contacts|length }})

{% for ct in contacts %}

{{ ct.name }} {% if ct.is_primary %}Haupt{% endif %}

{% if ct.role %}

{{ ct.role }}

{% endif %} {% if ct.email %}

{{ ct.email }}

{% endif %} {% if ct.phone %}

{{ ct.phone }}

{% endif %}
{% endfor %}
{% endif %} {% if customer.notes %}

Interne Notiz

{{ customer.notes }}

{% endif %}
{# v1.9.0: Self-Service-Portal-Karte #}

🌐 Self-Service-Portal

Kunde sieht eigene Aufträge, Rechnungen — eigene Anfragen stellen

{% if customer.portal_enabled and customer.portal_token %} {% set portal_url = (request.url.scheme + '://' + request.url.netloc + '/portal/' + customer.portal_token) %}

✓ Portal aktiv

Link für den Kunden:

↗ Öffnen
Token erneuern / deaktivieren
{% else %}

Generieren Sie einen Portal-Link für den Kunden. Damit hat er Read-only-Zugriff auf eigene Aufträge, kann Rechnungen herunterladen und neue Anfragen stellen — ohne dass Sie Logins verwalten müssen.

{% endif %}
{% endblock %}