Files
crm/templates/quote_edit.html
T

149 lines
8.6 KiB
HTML

{% extends 'base.html' %}
{% block title %}Offerte {{ qt.number }}{% endblock %}
{% macro row(l) %}
<tr>
<td data-label="Product">
<select name="l_product" class="prod form-select form-select-sm" aria-label="Product">
<option value="">Vrije regel</option>
{% for p in products %}
<option value="{{ p.id }}" {{ 'selected' if l.product_id == p.id }}
data-desc="{{ p.name }}{% if p.description %}&#10;{{ p.description }}{% endif %}"
data-price="{{ p.price }}" data-vat="{{ p.vat|nr|replace(',', '.') }}" data-unit="{{ p.unit }}">{{ p.name }}</option>
{% endfor %}
</select>
</td>
<td data-label="Omschrijving"><textarea class="form-control form-control-sm" name="l_desc" rows="1" aria-label="Omschrijving">{{ l.description }}</textarea></td>
<td data-label="Aantal"><input class="form-control form-control-sm" type="number" step="any" inputmode="decimal" name="l_qty" value="{{ l.qty|default(1) }}" aria-label="Aantal"></td>
<td data-label="Eenheid"><input class="form-control form-control-sm" name="l_unit" value="{{ l.unit }}" aria-label="Eenheid"></td>
<td data-label="Prijs excl."><input class="form-control form-control-sm" type="number" step="0.01" inputmode="decimal" name="l_price" value="{{ l.price }}" aria-label="Prijs">
{% if l.product_id and prices.get(l.product_id) is not none and prices[l.product_id] != l.price %}<p class="hint" style="margin:.2rem 0 0">Actueel: {{ prices[l.product_id]|euro }}</p>{% endif %}</td>
<td data-label="Btw"><select class="form-select form-select-sm" name="l_vat" aria-label="Btw">{% for v in [21, 9, 0] %}<option value="{{ v }}" {{ 'selected' if l.vat|default(21) == v }}>{{ v }}%</option>{% endfor %}</select></td>
<td class="num amt" data-label="Totaal"></td>
<td class="del-cell"><button type="button" class="linkbtn del" aria-label="Regel verwijderen">✕</button></td>
</tr>
{% endmacro %}
{% block content %}
<form method="post">
<div class="head">
<div><a class="label" href="{{ url_for('quotes') }}">← Offertes</a><h1>Offerte {{ qt.number }}</h1></div>
<div class="actions">
<button class="btn btn-outline-primary" name="then" value="print">Opslaan en afdrukken</button>
<button class="btn btn-primary">Opslaan</button>
</div>
</div>
<div class="card"><div class="card-body">
<div class="row g-3">
<div class="col-md-6"><label class="form-label" for="contact">Klant</label>
<select class="form-select" id="contact" name="contact_id">
<option value="">— Kies een contact —</option>
{% for c in contacts %}<option value="{{ c.id }}" {{ 'selected' if c.id == qt.contact_id }}>{{ c.name }}{% if c.company %} ({{ c.company }}){% endif %}</option>{% endfor %}
</select>
</div>
<div class="col-md-6"><label class="form-label" for="status">Status</label>
<select class="form-select" id="status" name="status">{% for k, l in QUOTE_STATUSES %}<option value="{{ k }}" {{ 'selected' if qt.status == k }}>{{ l }}</option>{% endfor %}</select>
<p class="hint">Op "Verzonden" zetten = zichtbaar in het klantportaal.</p>
</div>
<div class="col-6"><label class="form-label" for="date">Datum</label><input class="form-control" id="date" type="date" name="date" value="{{ qt.date }}"></div>
<div class="col-6"><label class="form-label" for="valid">Geldig tot</label><input class="form-control" id="valid" type="date" name="valid_until" value="{{ qt.valid_until or '' }}"></div>
<div class="col-12"><label class="form-label" for="title">Onderwerp</label><input class="form-control" id="title" name="title" value="{{ qt.title }}" placeholder="Bijv. Mantelzorgwoning model Amstel"></div>
<div class="col-12"><label class="form-label" for="intro">Inleiding</label><textarea class="form-control" id="intro" name="intro">{{ qt.intro }}</textarea></div>
</div>
</div></div>
<div class="card"><div class="card-body">
<h2>Regels</h2>
<div class="table-responsive"><table class="table lines">
<thead><tr><th style="min-width:170px">Product</th><th style="min-width:240px">Omschrijving</th><th style="width:90px">Aantal</th><th style="width:100px">Eenheid</th><th style="width:130px">Prijs excl.</th><th style="width:90px">Btw</th><th class="num" style="width:120px">Totaal</th><th></th></tr></thead>
<tbody id="rows">{% for l in lines %}{{ row(l) }}{% endfor %}</tbody>
</table></div>
<div class="actions"><button type="button" class="btn btn-outline-primary btn-sm" id="add">Regel toevoegen</button></div>
<table class="totals">
<tr><td>Subtotaal</td><td class="num" id="t-sub"></td></tr>
<tbody id="t-vat"></tbody>
<tr class="grand"><td>Totaal</td><td class="num" id="t-total"></td></tr>
</table>
</div></div>
<div class="card"><div class="card-body">
<label class="form-label" for="notes">Opmerkingen en voorwaarden</label>
<textarea class="form-control" id="notes" name="notes">{{ qt.notes }}</textarea>
</div></div>
</form>
<div class="card"><div class="card-body">
<div class="card-top">
<h2>Bestek uit ontwerp</h2>
<div class="actions">
{% if bestek %}
<a class="btn btn-outline-primary btn-sm" href="{{ url_for('quote_print', qid=qt.id) }}#bestek">Bekijken</a>
{% if designs %}<a class="btn btn-outline-primary btn-sm" href="{{ url_for('quote_bestek', qid=qt.id) }}">Opnieuw berekenen</a>{% endif %}
<form method="post" action="{{ url_for('quote_bestek_delete', qid=qt.id) }}" class="inline" onsubmit="return confirm('Bestek loskoppelen? De offerteregels blijven staan.')"><button class="btn btn-outline-secondary btn-sm">Loskoppelen</button></form>
{% elif designs %}
<a class="btn btn-primary btn-sm" href="{{ url_for('quote_bestek', qid=qt.id) }}">Bestek maken</a>
{% endif %}
</div>
</div>
{% if bestek %}
<p class="muted" style="margin:0">{{ bestek.design.title }} · berekend {{ bestek.computedAt[:19]|dt }} · {{ bestek.chapters|length }} hoofdstukken ·
{{ bestek.totals.exBtw|euro }} excl. btw ({{ bestek.totals.inclBtw|euro }} incl.). Het bestek staat als bijlage bij de offerte, ook in het klantportaal.</p>
{% elif designs %}
<p class="muted" style="margin:0">Hoeveelheden en prijzen uit het gekozen model van de klant (gevel, dak, kozijnen, stopcontacten, leidingen, …) als offerteregels en bijlage.</p>
{% else %}
<p class="empty" style="padding:0">Kies (en sla op) een klant met een gekozen model om een bestek uit het ontwerp te maken.</p>
{% endif %}
</div></div>
<form method="post" action="{{ url_for('quote_delete', qid=qt.id) }}" onsubmit="return confirm('Offerte verwijderen?')">
<button class="btn btn-outline-secondary btn-sm">Offerte verwijderen</button>
</form>
<template id="tpl">{{ row({}) }}</template>
<script>
const rows = document.getElementById('rows'), tpl = document.getElementById('tpl');
const fmt = n => '€ ' + n.toLocaleString('nl-NL', {minimumFractionDigits: 2, maximumFractionDigits: 2});
const val = (tr, name) => parseFloat(tr.querySelector(`[name=${name}]`).value) || 0;
function recalc() {
let sub = 0; const vat = {};
rows.querySelectorAll('tr').forEach(tr => {
const amount = val(tr, 'l_qty') * val(tr, 'l_price'), rate = val(tr, 'l_vat');
sub += amount;
vat[rate] = (vat[rate] || 0) + amount * rate / 100;
tr.querySelector('.amt').textContent = fmt(amount);
});
document.getElementById('t-sub').textContent = fmt(sub);
const tv = document.getElementById('t-vat');
tv.innerHTML = '';
let total = sub;
Object.keys(vat).sort((a, b) => b - a).forEach(rate => {
total += vat[rate];
const r = tv.insertRow();
r.insertCell().textContent = `Btw ${rate}%`;
const c = r.insertCell(); c.className = 'num'; c.textContent = fmt(vat[rate]);
});
document.getElementById('t-total').textContent = fmt(total);
}
rows.addEventListener('input', recalc);
rows.addEventListener('change', e => {
if (e.target.classList.contains('prod') && e.target.value) {
const o = e.target.selectedOptions[0], tr = e.target.closest('tr');
tr.querySelector('[name=l_desc]').value = o.dataset.desc;
tr.querySelector('[name=l_price]').value = o.dataset.price;
tr.querySelector('[name=l_vat]').value = o.dataset.vat;
tr.querySelector('[name=l_unit]').value = o.dataset.unit;
}
recalc();
});
rows.addEventListener('click', e => {
if (e.target.closest('.del')) { e.target.closest('tr').remove(); recalc(); }
});
document.getElementById('add').addEventListener('click', () => {
const tr = tpl.content.firstElementChild.cloneNode(true);
rows.appendChild(tr);
document.dispatchEvent(new CustomEvent('crm:row', { detail: tr }));
recalc();
});
if (!rows.children.length) document.getElementById('add').click(); else recalc();
</script>
{% endblock %}