217 lines
10 KiB
Plaintext
217 lines
10 KiB
Plaintext
{block content}
|
|
|
|
<!-- Načtení SortableJS pro Drag & Drop -->
|
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"></script>
|
|
|
|
<div class="container-fluid">
|
|
<div class="content-card my-3">
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h2 class="h4 mb-0">
|
|
<i class="bi {if $isEdit}bi-pencil-square{else}bi-file-earmark-plus-fill{/if} me-2"></i>
|
|
{if $isEdit}Úprava faktury{else}Nová faktura{/if}
|
|
</h2>
|
|
<a n:href="Faktura:default" class="btn btn-outline-secondary btn-sm">
|
|
<i class="bi bi-arrow-left me-1"></i>Zpět na seznam
|
|
</a>
|
|
</div>
|
|
|
|
<form n:name="fakturaForm" id="faktura-form" class="row g-3">
|
|
<div n:if="$form->hasErrors()" class="col-12">
|
|
<div class="alert alert-danger mb-0">
|
|
<ul class="mb-0">
|
|
<li n:foreach="$form->errors as $error">{$error}</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hlavička Faktury -->
|
|
<div class="col-md-6">
|
|
<label n:name="ZAKAZNIK_ID" class="form-label fw-bold">Odběratel *</label>
|
|
<select n:name="ZAKAZNIK_ID" class="form-select"></select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label n:name="CISLO" class="form-label fw-bold">Číslo faktury *</label>
|
|
<input n:name="CISLO" class="form-control">
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<label n:name="DATUM_VYSTAVENI" class="form-label">Datum vystavení</label>
|
|
<input n:name="DATUM_VYSTAVENI" class="form-control">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label n:name="DATUM_SPLATNOSTI" class="form-label">Datum splatnosti</label>
|
|
<input n:name="DATUM_SPLATNOSTI" class="form-control">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label n:name="STAV" class="form-label">Stav úhrady</label>
|
|
<select n:name="STAV" class="form-select"></select>
|
|
</div>
|
|
|
|
<hr class="my-4">
|
|
|
|
<!-- Položky Faktury -->
|
|
<div class="col-12">
|
|
<h5 class="mb-3"><i class="bi bi-list-check me-2"></i>Položky faktury</h5>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered align-middle" id="items-table">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th style="width: 40px;" class="text-center"></th> <!-- Sloupec pro Drag Handle -->
|
|
<th>Název položky / služby</th>
|
|
<th style="width: 100px;">Počet</th>
|
|
<th style="width: 90px;">Jedn.</th>
|
|
<th style="width: 130px;">Cena/Jedn.</th>
|
|
<th style="width: 100px;">Sleva %</th>
|
|
<th style="width: 130px;" class="text-end">Celkem</th>
|
|
<th style="width: 50px;"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="items-body">
|
|
{foreach $polozky as $index => $p}
|
|
<tr class="item-row">
|
|
<td class="text-center align-middle drag-handle" style="cursor: grab;">
|
|
<i class="bi bi-grip-vertical text-muted fs-5"></i>
|
|
</td>
|
|
<td><input type="text" name="polozky[{$index}][NAZEV]" value="{$p->NAZEV}" class="form-control item-nazev" required></td>
|
|
<td><input type="number" step="0.01" name="polozky[{$index}][POCET]" value="{$p->POCET}" class="form-control item-pocet text-end" required></td>
|
|
<td><input type="text" name="polozky[{$index}][JEDNOTKA]" value="{$p->JEDNOTKA}" class="form-control"></td>
|
|
<td><input type="number" step="0.01" name="polozky[{$index}][CENA_JEDNOTKA]" value="{$p->CENA_JEDNOTKA}" class="form-control item-cena text-end" required></td>
|
|
<td><input type="number" step="0.1" name="polozky[{$index}][SLEVA]" value="{$p->SLEVA}" class="form-control item-sleva text-end"></td>
|
|
<td class="text-end fw-bold item-celkem-text">{$p->CENA|number:2, ',', ' '} CZK</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-row"><i class="bi bi-trash"></i></button>
|
|
</td>
|
|
</tr>
|
|
{/foreach}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<td colspan="6" class="text-end fw-bold fs-5">Celková částka:</td>
|
|
<td class="text-end fw-bold fs-5 text-primary" id="total-sum">0,00 CZK</td>
|
|
<td></td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-outline-success btn-sm" id="btn-add-item">
|
|
<i class="bi bi-plus-circle me-1"></i>Přidat položku
|
|
</button>
|
|
</div>
|
|
|
|
<hr class="my-4">
|
|
|
|
<div class="col-12">
|
|
<label n:name="CINNOSTI" class="form-label fw-bold">Činnosti / Popis plnění</label>
|
|
<textarea n:name="CINNOSTI" class="form-control" rows="3" placeholder="Rozsah vykonaných prací..."></textarea>
|
|
</div>
|
|
|
|
<div class="col-12 mt-4">
|
|
<button n:name="save" class="btn btn-primary me-2">
|
|
<i class="bi bi-check-lg me-1"></i>Uložit fakturu
|
|
</button>
|
|
<a n:href="Faktura:default" class="btn btn-outline-secondary">Zrušit</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{syntax off}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
let rowIndex = document.querySelectorAll('.item-row').length;
|
|
|
|
const tbody = document.getElementById('items-body');
|
|
const btnAdd = document.getElementById('btn-add-item');
|
|
|
|
// 1. Inicializace SortableJS na <tbody>
|
|
if (typeof Sortable !== 'undefined') {
|
|
new Sortable(tbody, {
|
|
handle: '.drag-handle', // Přetahovat lze uchopením za ikonu
|
|
animation: 150,
|
|
ghostClass: 'table-active', // Zvýraznění přetahovaného řádku
|
|
onEnd: function () {
|
|
reindexRows();
|
|
calculateTotals();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 2. Přereindexování name atributů podle nového pořadí řádků v DOMu
|
|
function reindexRows() {
|
|
document.querySelectorAll('.item-row').forEach((row, index) => {
|
|
row.querySelectorAll('input').forEach(input => {
|
|
const name = input.getAttribute('name');
|
|
if (name) {
|
|
input.setAttribute('name', name.replace(/polozky\[\d+\]/, `polozky[${index}]`));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 3. Výpočet součtů
|
|
function calculateTotals() {
|
|
let grandTotal = 0;
|
|
|
|
document.querySelectorAll('.item-row').forEach(row => {
|
|
const pocet = parseFloat(row.querySelector('.item-pocet').value) || 0;
|
|
const cena = parseFloat(row.querySelector('.item-cena').value) || 0;
|
|
const sleva = parseFloat(row.querySelector('.item-sleva').value) || 0;
|
|
|
|
const celkem = (pocet * cena) * (1 - (sleva / 100));
|
|
grandTotal += celkem;
|
|
|
|
row.querySelector('.item-celkem-text').textContent = celkem.toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' CZK';
|
|
});
|
|
|
|
document.getElementById('total-sum').textContent = grandTotal.toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' CZK';
|
|
}
|
|
|
|
// Přidání nového řádku
|
|
btnAdd.addEventListener('click', () => {
|
|
const tr = document.createElement('tr');
|
|
tr.className = 'item-row';
|
|
tr.innerHTML = `
|
|
<td class="text-center align-middle drag-handle" style="cursor: grab;">
|
|
<i class="bi bi-grip-vertical text-muted fs-5"></i>
|
|
</td>
|
|
<td><input type="text" name="polozky[${rowIndex}][NAZEV]" class="form-control item-nazev" placeholder="Popis práce / zboží" required></td>
|
|
<td><input type="number" step="0.01" name="polozky[${rowIndex}][POCET]" value="1" class="form-control item-pocet text-end" required></td>
|
|
<td><input type="text" name="polozky[${rowIndex}][JEDNOTKA]" value="ks" class="form-control"></td>
|
|
<td><input type="number" step="0.01" name="polozky[${rowIndex}][CENA_JEDNOTKA]" value="0" class="form-control item-cena text-end" required></td>
|
|
<td><input type="number" step="0.1" name="polozky[${rowIndex}][SLEVA]" value="0" class="form-control item-sleva text-end"></td>
|
|
<td class="text-end fw-bold item-celkem-text">0,00 CZK</td>
|
|
<td class="text-center">
|
|
<button type="button" class="btn btn-outline-danger btn-sm btn-remove-row"><i class="bi bi-trash"></i></button>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
rowIndex++;
|
|
reindexRows();
|
|
calculateTotals();
|
|
});
|
|
|
|
// Event delegation pro smazání a přepočet
|
|
tbody.addEventListener('click', (e) => {
|
|
if (e.target.closest('.btn-remove-row')) {
|
|
e.target.closest('.item-row').remove();
|
|
reindexRows();
|
|
calculateTotals();
|
|
}
|
|
});
|
|
|
|
tbody.addEventListener('input', (e) => {
|
|
if (e.target.matches('.item-pocet, .item-cena, .item-sleva')) {
|
|
calculateTotals();
|
|
}
|
|
});
|
|
|
|
if (document.querySelectorAll('.item-row').length === 0) {
|
|
btnAdd.click();
|
|
} else {
|
|
calculateTotals();
|
|
}
|
|
});
|
|
</script>
|
|
{/syntax} |