Kufrik/app/UI/FakturaEdit/edit.latte

309 lines
16 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">
<input n:name="CISLO_ABS">
</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" n:if="$isEdit">
<label class="form-label">Stav úhrady</label>
<div class="form-control bg-light">
<span class="badge {if $stavUhrady === 'PAID'}bg-success{elseif $stavUhrady === 'PARTIAL'}bg-info text-dark{else}bg-warning text-dark{/if}">
{if $stavUhrady === 'PAID'}Zaplaceno{elseif $stavUhrady === 'PARTIAL'}Částečně uhrazeno{else}Neuhrazeno{/if}
</span>
<span class="ms-2 text-muted">zbývá {$zbyva|number:2, ',', ' '} CZK</span>
</div>
</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 table-cards 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 data-label="Pořadí" class="text-center align-middle drag-handle" style="cursor: grab;">
<i class="bi bi-grip-vertical text-muted fs-5"></i>
</td>
<td data-label="Název"><input type="text" name="polozky[{$index}][NAZEV]" value="{$p->NAZEV}" class="form-control item-nazev" required></td>
<td data-label="Počet"><input type="number" step="0.01" name="polozky[{$index}][POCET]" value="{$p->POCET}" class="form-control item-pocet text-end" required></td>
<td data-label="Jedn."><input type="text" name="polozky[{$index}][JEDNOTKA]" value="{$p->JEDNOTKA}" class="form-control"></td>
<td data-label="Cena/Jedn."><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 data-label="Sleva %"><input type="number" step="0.1" name="polozky[{$index}][SLEVA]" value="{$p->SLEVA}" class="form-control item-sleva text-end"></td>
<td data-label="Celkem" class="text-end fw-bold item-celkem-text">{$p->CENA|number:2, ',', ' '} CZK</td>
<td class="text-center cell-actions">
<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 cell-span">Celková částka:</td>
<td class="text-end fw-bold fs-5 text-primary" id="total-sum">0,00 CZK</td>
<td class="cell-actions"></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 n:if="$isEdit" id="uhrady" class="mt-4 pt-3 border-top">
<h5 class="mb-3"><i class="bi bi-cash-coin me-2"></i>Úhrady</h5>
<p class="text-muted small">
Celkem {$celkemFaktury|number:2, ',', ' '} CZK,
uhrazeno {$zaplaceno|number:2, ',', ' '} CZK,
zbývá {$zbyva|number:2, ',', ' '} CZK.
</p>
<form n:name="platbyForm">
<div n:if="$presenter['platbyForm']->hasErrors()" class="alert alert-danger">
<ul class="mb-0">
<li n:foreach="$presenter['platbyForm']->errors as $error">{$error}</li>
</ul>
</div>
<div class="table-responsive mb-3">
<table class="table table-sm table-cards align-middle">
<thead class="table-light">
<tr>
<th style="width: 160px;">Datum</th>
<th style="width: 160px;" class="text-end">Částka</th>
<th>Poznámka</th>
<th style="width: 50px;"></th>
</tr>
</thead>
<tbody>
<tr n:if="count($platby) === 0">
<td colspan="4" class="text-muted cell-span">Zatím žádná platba.</td>
</tr>
<tr n:foreach="$form['platby']->getComponents() as $platbaId => $row">
<td data-label="Datum">{input $row['datum'] class: 'form-control'}</td>
<td data-label="Částka">{input $row['castka'] class: 'form-control text-end'}</td>
<td data-label="Poznámka">{input $row['poznamka'] class: 'form-control'}</td>
<td class="text-end cell-actions">
<a n:href="smazatPlatbu! platbaId => $platbaId"
class="btn btn-sm btn-outline-danger"
title="Smazat platbu"
onclick="return confirm('Opravdu smazat tuto platbu?');">
<i class="bi bi-trash"></i>
</a>
</td>
</tr>
<tr>
<td data-label="Datum">
{input $form['nova']['datum'] class: 'form-control'}
</td>
<td data-label="Částka">
{input $form['nova']['castka'] class: 'form-control text-end'}
</td>
<td data-label="Poznámka">
{input $form['nova']['poznamka'] class: 'form-control', placeholder: 'Nová platba'}
</td>
<td class="cell-actions"></td>
</tr>
</tbody>
</table>
</div>
<div class="mb-3">
<button type="button" class="btn btn-sm btn-outline-secondary me-1" id="edit-platba-dnes" data-zbyva="{$zbyvaInput}">Dnes</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="edit-platba-splatnost" data-date="{$splatnostIso}" data-zbyva="{$zbyvaInput}">Den splatnosti</button>
</div>
<button n:name="save" class="btn btn-success">
<i class="bi bi-check-lg me-1"></i>Uložit platby
</button>
</form>
</div>
</div>
</div>
<script n:if="$isEdit">
function prefillPlatba(date, zbyva) {
if (date) {
document.getElementById('edit-platba-datum').value = date;
}
const castka = Number(zbyva);
document.getElementById('edit-platba-castka').value = castka > 0 ? castka.toFixed(2) : '';
}
document.getElementById('edit-platba-dnes').addEventListener('click', function () {
const d = new Date();
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
prefillPlatba(d.getFullYear() + '-' + m + '-' + day, this.getAttribute('data-zbyva'));
});
document.getElementById('edit-platba-splatnost').addEventListener('click', function () {
prefillPlatba(this.getAttribute('data-date'), this.getAttribute('data-zbyva'));
});
</script>
{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 data-label="Pořadí" class="text-center align-middle drag-handle" style="cursor: grab;">
<i class="bi bi-grip-vertical text-muted fs-5"></i>
</td>
<td data-label="Název"><input type="text" name="polozky[${rowIndex}][NAZEV]" class="form-control item-nazev" placeholder="Popis práce / zboží" required></td>
<td data-label="Počet"><input type="number" step="0.01" name="polozky[${rowIndex}][POCET]" value="1" class="form-control item-pocet text-end" required></td>
<td data-label="Jedn."><input type="text" name="polozky[${rowIndex}][JEDNOTKA]" value="ks" class="form-control"></td>
<td data-label="Cena/Jedn."><input type="number" step="0.01" name="polozky[${rowIndex}][CENA_JEDNOTKA]" value="0" class="form-control item-cena text-end" required></td>
<td data-label="Sleva %"><input type="number" step="0.1" name="polozky[${rowIndex}][SLEVA]" value="0" class="form-control item-sleva text-end"></td>
<td data-label="Celkem" class="text-end fw-bold item-celkem-text">0,00 CZK</td>
<td class="text-center cell-actions">
<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}