75 lines
5.4 KiB
HTML
75 lines
5.4 KiB
HTML
{% extends 'base.html' %}
|
||
{% block head %}
|
||
{# De engine haalt kosten/*.csv en assets/ relatief op: base = de gekopieerde configurator. Alle CRM-links zijn absoluut. #}
|
||
<base href="{{ url_for('static', filename='configurator/') }}">
|
||
{% endblock %}
|
||
{% block title %}Bestek · Offerte {{ qt.number }}{% endblock %}
|
||
{% block styles %}
|
||
<style>
|
||
.bestek-page { max-width: 980px; }
|
||
.bs-ch { margin-bottom: 1.5rem; }
|
||
.bs-ch-head { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; border-bottom: 2px solid var(--ink); padding-bottom: 0.3rem; margin-bottom: 0.25rem; }
|
||
.bs-ch-head h2 { margin: 0; font-size: 1.1rem; }
|
||
.bs-ch table { width: 100%; border-collapse: collapse; }
|
||
.bs-ch td { padding: 0.35rem 0.25rem; border-bottom: 1px solid var(--line); vertical-align: top; }
|
||
.bs-ch td.num { text-align: right; white-space: nowrap; width: 9rem; }
|
||
.bs-info td { color: var(--ink-muted); font-style: italic; }
|
||
.bs-ch td.num.muted { width: 7rem; }
|
||
.bs-ch .chip { text-transform: none; letter-spacing: 0; font-weight: 400; font-size: 0.7rem; margin-left: 0.3rem; }
|
||
.bs-total { margin-left: auto; width: 320px; max-width: 100%; }
|
||
.bs-total div { display: flex; justify-content: space-between; padding: 0.2rem 0; }
|
||
.bs-total .grand { border-top: 2px solid var(--ink); margin-top: 0.3rem; padding-top: 0.5rem; font-family: var(--font-heading); font-size: 1.15rem; }
|
||
.bs-opts .form-check { margin-bottom: 0.4rem; }
|
||
@media (max-width: 640px) { .bs-ch td.num { width: auto; } }
|
||
</style>
|
||
{% endblock %}
|
||
{% block content %}
|
||
<div class="bestek-page">
|
||
<div class="head">
|
||
<div>
|
||
<a class="label" href="{{ url_for('quote_edit', qid=qt.id) }}">← Offerte {{ qt.number }}</a>
|
||
<h1>Bestek uit ontwerp</h1>
|
||
<div class="muted">{{ design_file.title }}{% if c %} · {{ c.name }}{% endif %} · ontvangen {{ design_file.created_at|dt }}</div>
|
||
</div>
|
||
{% if designs|length > 1 %}
|
||
<form method="get" class="actions" action="{{ url_for('quote_bestek', qid=qt.id) }}">
|
||
<select class="form-select w-auto" name="file" onchange="this.form.submit()" aria-label="Ontwerp">
|
||
{% for d in designs %}<option value="{{ d.id }}" {{ 'selected' if d.id == design_file.id }}>{{ d.title }} · {{ d.created_at|dt('%d-%m-%Y') }}</option>{% endfor %}
|
||
</select>
|
||
</form>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div id="bestek">
|
||
<div class="card"><div class="card-body">
|
||
<p class="muted">Hoeveelheden en prijzen uit de configurator (zelfde berekening als Kosten & tijden: kostprijs met
|
||
algemene kosten en marge, prijzen uit de kosten-CSV's). Bedragen excl. btw. Schroeven, zaag- en boorwerk staan per hoofdstuk samen onder "Bevestiging en bewerking".
|
||
Posten met een code worden gekoppeld aan het product met die SKU (bestaat het nog niet, dan wordt het aangemaakt).</p>
|
||
<div class="bs-body">{% if engine %}<p class="empty">Bestek berekenen…</p>{% else %}<p class="empty">De 3D-engine van de configurator ontbreekt. Draai tools/configurator-sync.sh in de CRM-map.</p>{% endif %}</div>
|
||
<div class="bs-total"></div>
|
||
</div></div>
|
||
|
||
<form method="post" action="{{ url_for('quote_bestek_save', qid=qt.id) }}" class="card bs-opts"><div class="card-body">
|
||
<input type="hidden" name="bestek">
|
||
<input type="hidden" name="file_id" value="{{ design_file.id }}">
|
||
<h2>In de offerte zetten</h2>
|
||
<div class="form-check"><input class="form-check-input" type="radio" id="mode-post" name="mode" value="post" checked><label class="form-check-label" for="mode-post"><strong>Per post</strong>: elke regel van het bestek als offerteregel met aantal en prijs per eenheid (bijv. 42 st. × prijs per spot)</label></div>
|
||
<div class="form-check"><input class="form-check-input" type="radio" id="mode-ch" name="mode" value="hoofdstuk"><label class="form-check-label" for="mode-ch"><strong>Per hoofdstuk</strong>: één regel per hoofdstuk (1 post) met de posten in de omschrijving</label></div>
|
||
<div class="form-check"><input class="form-check-input" type="checkbox" id="opt-replace" name="replace" value="1" {{ 'checked' if has_lines }}><label class="form-check-label" for="opt-replace">Bestaande regels van de offerte vervangen{% if has_lines %} ({{ has_lines }} regel{{ 's' if has_lines != 1 }}){% endif %}</label></div>
|
||
<div class="form-check"><input class="form-check-input" type="checkbox" id="opt-prices" name="update_prices" value="1"><label class="form-check-label" for="opt-prices">Prijzen van bestaande producten bijwerken naar deze berekening (wordt vastgelegd in de producthistorie)</label></div>
|
||
<p class="hint">Het bestek (alle posten met hoeveelheden) komt als bijlage bij de offerte, ook in de afdrukversie en het klantportaal.</p>
|
||
<div class="actions"><button class="btn btn-primary" type="submit" disabled>In offerte zetten</button>
|
||
<a class="btn btn-outline-secondary" href="{{ url_for('quote_edit', qid=qt.id) }}">Annuleren</a></div>
|
||
</div></form>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
{% block scripts %}
|
||
{% if engine %}
|
||
<script type="application/json" id="design">{{ design|tojson }}</script>
|
||
<script type="application/json" id="bestek-meta">{{ {'design': {'file_id': design_file.id, 'title': design_file.title}, 'configurator': design.get('configurator') or ''}|tojson }}</script>
|
||
{% for src in scripts %}<script src="{{ src }}"></script>
|
||
{% endfor %}<script src="{{ url_for('static', filename='bestek.js') }}"></script>
|
||
{% endif %}
|
||
{% endblock %}
|