Přepočítávání a GUI faktury.

master
Kowalski 2026-10-31 11:16:31 +01:00
parent 3257006a02
commit e1da2619a8
1 changed files with 67 additions and 14 deletions

View File

@ -69,7 +69,7 @@
<th style="width: 90px;">Jedn.</th> <th style="width: 90px;">Jedn.</th>
<th style="width: 130px;">Cena/Jedn.</th> <th style="width: 130px;">Cena/Jedn.</th>
<th style="width: 100px;">Sleva %</th> <th style="width: 100px;">Sleva %</th>
<th style="width: 130px;" class="text-end">Celkem</th> <th style="width: 170px;" class="text-end">Celkem</th>
<th style="width: 50px;"></th> <th style="width: 50px;"></th>
</tr> </tr>
</thead> </thead>
@ -80,11 +80,11 @@
<i class="bi bi-grip-vertical text-muted fs-5"></i> <i class="bi bi-grip-vertical text-muted fs-5"></i>
</td> </td>
<td data-label="Název"><input type="text" name="polozky[{$index}][NAZEV]" value="{$p->NAZEV}" class="form-control item-nazev" placeholder="Začněte psát název práce nebo zboží" required autocomplete="off"></td> <td data-label="Název"><input type="text" name="polozky[{$index}][NAZEV]" value="{$p->NAZEV}" class="form-control item-nazev" placeholder="Začněte psát název práce nebo zboží" required autocomplete="off"></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="Počet"><input type="number" step="any" 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="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="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="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 data-label="Celkem"><input type="number" step="0.01" value="{$p->CENA}" class="form-control item-celkem text-end fw-bold" title="Úprava přepočítá počet"></td>
<td class="text-center cell-actions"> <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> <button type="button" class="btn btn-outline-danger btn-sm btn-remove-row"><i class="bi bi-trash"></i></button>
</td> </td>
@ -94,8 +94,7 @@
<tfoot> <tfoot>
<tr> <tr>
<td colspan="6" class="text-end fw-bold fs-5 cell-span">Celková částka:</td> <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 colspan="2" class="text-end fw-bold fs-5 text-primary text-nowrap" id="total-sum">0,00 CZK</td>
<td class="cell-actions"></td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
@ -244,24 +243,66 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
} }
// 3. Výpočet součtů // 3. Výpočet součtů (celkem ← počet × cena × (1 − sleva))
function roundMoney(value) {
return Math.round((value + Number.EPSILON) * 100) / 100;
}
function roundPocet(value) {
return Math.round((value + Number.EPSILON) * 10000) / 10000;
}
function rowCelkem(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;
return (pocet * cena) * (1 - (sleva / 100));
}
function calculateTotals() { function calculateTotals() {
let grandTotal = 0; let grandTotal = 0;
document.querySelectorAll('.item-row').forEach(row => { document.querySelectorAll('.item-row').forEach(row => {
const pocet = parseFloat(row.querySelector('.item-pocet').value) || 0; const celkem = rowCelkem(row);
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; grandTotal += celkem;
row.querySelector('.item-celkem-text').textContent = celkem.toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' CZK'; const celkemInput = row.querySelector('.item-celkem');
if (celkemInput && document.activeElement !== celkemInput) {
celkemInput.value = roundMoney(celkem).toFixed(2);
}
}); });
document.getElementById('total-sum').textContent = grandTotal.toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' CZK'; document.getElementById('total-sum').textContent = grandTotal.toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' CZK';
} }
// Úprava celkově: přepočítá počet, cena/jedn. a sleva zůstávají
function recalculatePocetFromCelkem(row) {
const celkem = parseFloat(row.querySelector('.item-celkem').value) || 0;
const cena = parseFloat(row.querySelector('.item-cena').value) || 0;
const sleva = parseFloat(row.querySelector('.item-sleva').value) || 0;
const faktor = cena * (1 - (sleva / 100));
const pocetInput = row.querySelector('.item-pocet');
if (!pocetInput || Math.abs(faktor) < 1e-9) {
return;
}
pocetInput.value = String(roundPocet(celkem / faktor));
}
function updateGrandTotalFromInputs() {
let grandTotal = 0;
document.querySelectorAll('.item-row').forEach(row => {
const celkemInput = row.querySelector('.item-celkem');
if (celkemInput && document.activeElement === celkemInput) {
grandTotal += parseFloat(celkemInput.value) || 0;
} else {
grandTotal += rowCelkem(row);
}
});
document.getElementById('total-sum').textContent = grandTotal.toLocaleString('cs-CZ', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' CZK';
}
function fold(value) { function fold(value) {
return String(value).normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase(); return String(value).normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
} }
@ -593,11 +634,11 @@ document.addEventListener('DOMContentLoaded', () => {
<i class="bi bi-grip-vertical text-muted fs-5"></i> <i class="bi bi-grip-vertical text-muted fs-5"></i>
</td> </td>
<td data-label="Název"><input type="text" name="polozky[${rowIndex}][NAZEV]" class="form-control item-nazev" placeholder="Začněte psát název práce nebo zboží" required autocomplete="off"></td> <td data-label="Název"><input type="text" name="polozky[${rowIndex}][NAZEV]" class="form-control item-nazev" placeholder="Začněte psát název práce nebo zboží" required autocomplete="off"></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="Počet"><input type="number" step="any" 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="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="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="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 data-label="Celkem"><input type="number" step="0.01" value="0" class="form-control item-celkem text-end fw-bold" title="Úprava přepočítá počet"></td>
<td class="text-center cell-actions"> <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> <button type="button" class="btn btn-outline-danger btn-sm btn-remove-row"><i class="bi bi-trash"></i></button>
</td> </td>
@ -621,11 +662,23 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
tbody.addEventListener('input', (e) => { tbody.addEventListener('input', (e) => {
if (e.target.matches('.item-celkem')) {
recalculatePocetFromCelkem(e.target.closest('.item-row'));
updateGrandTotalFromInputs();
return;
}
if (e.target.matches('.item-pocet, .item-cena, .item-sleva')) { if (e.target.matches('.item-pocet, .item-cena, .item-sleva')) {
calculateTotals(); calculateTotals();
} }
}); });
tbody.addEventListener('focusout', (e) => {
if (e.target.matches('.item-celkem')) {
recalculatePocetFromCelkem(e.target.closest('.item-row'));
calculateTotals();
}
});
if (document.querySelectorAll('.item-row').length === 0) { if (document.querySelectorAll('.item-row').length === 0) {
btnAdd.click(); btnAdd.click();
} else { } else {