Files
ksef_app/app/templates/invoices/issued_form.html
Mateusz Gruszczyński 35571df778 push
2026-03-13 11:03:13 +01:00

121 lines
7.8 KiB
HTML

{% extends 'base.html' %}
{% block title %}<i class="fa-solid fa-square-plus me-2 text-primary"></i>{{ 'Edytuj fakturę' if editing_invoice else 'Wystaw fakturę' }}{% endblock %}
{% block content %}
{% set eyebrow='Sprzedaż' %}{% set heading=('Edytuj fakturę' if editing_invoice else 'Wystaw fakturę') %}{% set description='Widok uproszczony, dopasowany do stylu panelu administracyjnego.' %}
{% include 'partials/page_header.html' with context %}
<div class="row g-4">
<div class="col-xl-8">
<div class="card">
<div class="card-body">
{% if read_only_mode %}<div class="alert alert-warning">Tryb tylko do odczytu - wystawianie faktur jest zablokowane.</div>{% endif %}
<form method="post">
{{ form.hidden_tag() }}
<div class="row g-3">
<div class="col-md-6">
<div class="d-flex justify-content-between align-items-center mb-1">
{{ form.customer_id.label(class='form-label mb-0') }}
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#customerQuickAddModal"><i class="fa-solid fa-plus me-1"></i>Dodaj</button>
</div>
{{ form.customer_id(class='form-select', disabled=read_only_mode) }}
<div class="form-text"><a href="{{ url_for('invoices.customers') }}">Pełna kartoteka klientów</a></div>
</div>
<div class="col-md-6">{{ form.numbering_template.label(class='form-label') }}{{ form.numbering_template(class='form-select', disabled=read_only_mode) }}</div>
<div class="col-md-6">{{ form.invoice_number.label(class='form-label') }}{{ form.invoice_number(class='form-control', disabled=read_only_mode, placeholder=preview_number) }}<div class="form-text">Puste pole = numer zostanie nadany automatycznie.</div></div>
<div class="col-md-6 d-flex align-items-end"><div class="alert alert-secondary w-100 mb-0 small">Proponowany numer: <strong>{{ preview_number or '—' }}</strong></div></div>
<div class="col-md-6">
<div class="d-flex justify-content-between align-items-center mb-1">
{{ form.product_id.label(class='form-label mb-0') }}
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#productQuickAddModal"><i class="fa-solid fa-plus me-1"></i>Dodaj</button>
</div>
<select class="form-select" id="productField" name="{{ form.product_id.name }}" {% if read_only_mode %}disabled{% endif %}>
{% for product in products %}
<option value="{{ product.id }}" data-net-price="{{ product.net_price }}" data-vat-rate="{{ product.vat_rate }}" data-split-payment="{{ 'true' if product.split_payment_default else 'false' }}" {{ 'selected' if form.product_id.data == product.id else '' }}>{{ product.name }} - {{ product.net_price }} PLN</option>
{% endfor %}
</select>
<div class="form-text"><a href="{{ url_for('invoices.products') }}">Pełna kartoteka towarów i usług</a></div>
</div>
<div class="col-md-3">{{ form.quantity.label(class='form-label') }}{{ form.quantity(class='form-control', disabled=read_only_mode, id='quantityField') }}</div>
<div class="col-md-3">{{ form.unit_net.label(class='form-label') }}{{ form.unit_net(class='form-control', disabled=read_only_mode, id='unitNetField') }}</div>
<div class="col-12">
<div class="form-check form-switch">
{{ form.split_payment(class='form-check-input', disabled=read_only_mode, id='splitPaymentField') }}
{{ form.split_payment.label(class='form-check-label') }}
</div>
<div class="form-text" id="splitPaymentHint">Domyślnie włączane dla usług oznaczonych w kartotece. Dla faktur powyżej 15 000 PLN brutto jest wymuszane.</div>
</div>
</div>
<div class="mt-3 d-flex gap-2 flex-wrap">{% if editing_invoice %}<button class="btn btn-outline-primary" name="save_submit" type="submit" {% if read_only_mode %}disabled{% endif %}>Zapisz zmiany</button><button class="btn btn-primary" name="submit" type="submit" {% if read_only_mode %}disabled{% endif %}>Zapisz i wyślij do KSeF</button>{% else %}{{ form.save_submit(class='btn btn-outline-primary', disabled=read_only_mode) }}{{ form.submit(class='btn btn-primary', disabled=read_only_mode) }}{% endif %}</div>
</form>
</div>
</div>
</div>
<div class="col-xl-4">
<div class="card h-100">
<div class="card-header"><i class="fa-solid fa-circle-info me-2"></i>Podpowiedzi</div>
<div class="card-body small text-secondary d-flex flex-column gap-3">
<div>Dodawanie klientów działa teraz przez wspólne formularze dostępne także w formularzu NFZ. (jeśli moduł włączony)</div>
<div>Po zapisaniu nowy klient lub towar zostanie automatycznie podstawiony do formularza.</div>
<div>Pełne kartoteki nadal są dostępne z linków pod polami wyboru.</div>
</div>
</div>
</div>
</div>
{% set quick_return_endpoint = 'invoices.issued_edit' if editing_invoice else 'invoices.issued_new' %}
{% set quick_invoice_id = editing_invoice.id if editing_invoice else None %}
{% include 'partials/invoice_quick_add_modals.html' %}
<script>
(() => {
const productField = document.getElementById('productField');
const quantityField = document.getElementById('quantityField');
const unitNetField = document.getElementById('unitNetField');
const splitPaymentField = document.getElementById('splitPaymentField');
const splitPaymentHint = document.getElementById('splitPaymentHint');
if (!productField || !quantityField || !unitNetField || !splitPaymentField) return;
let userModifiedSplit = false;
const parseNumber = (value) => {
const normalized = String(value || '').replace(/\s+/g, '').replace(',', '.');
const parsed = Number.parseFloat(normalized);
return Number.isFinite(parsed) ? parsed : 0;
};
const syncSplitPayment = (resetToProductDefault = false) => {
const selected = productField.options[productField.selectedIndex];
const vatRate = parseNumber(selected?.dataset?.vatRate || 0);
const defaultSplit = (selected?.dataset?.splitPayment || 'false') === 'true';
const gross = parseNumber(quantityField.value) * parseNumber(unitNetField.value) * (1 + vatRate / 100);
const forced = gross > 15000;
if (forced) {
splitPaymentField.checked = true;
splitPaymentField.disabled = true;
if (splitPaymentHint) splitPaymentHint.textContent = 'Split payment jest wymagany, ponieważ wartość brutto faktury przekracza 15 000 PLN.';
return;
}
splitPaymentField.disabled = {{ 'true' if read_only_mode else 'false' }};
if (resetToProductDefault || !userModifiedSplit) {
splitPaymentField.checked = defaultSplit;
}
if (splitPaymentHint) {
splitPaymentHint.textContent = defaultSplit
? 'Dla wybranej usługi split payment jest domyślnie włączony. Możesz go zmienić dla tej faktury.'
: 'Split payment możesz włączyć ręcznie. Dla faktur powyżej 15 000 PLN brutto jest wymuszany.';
}
};
productField.addEventListener('change', () => {
const selected = productField.options[productField.selectedIndex];
if (selected?.dataset?.netPrice && !unitNetField.value) unitNetField.value = selected.dataset.netPrice;
userModifiedSplit = false;
syncSplitPayment(true);
});
quantityField.addEventListener('input', () => syncSplitPayment(false));
unitNetField.addEventListener('input', () => syncSplitPayment(false));
splitPaymentField.addEventListener('change', () => {
userModifiedSplit = true;
});
syncSplitPayment(false);
})();
</script>
{% endblock %}