UI cleanup
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -432,6 +432,7 @@ def log_payment():
|
||||
total = float(data.get('total', 0))
|
||||
balance_before = float(data.get('balance_before', 0))
|
||||
balance_after = float(data.get('balance_after', 0))
|
||||
buyer = (data.get('buyer') or '').strip()
|
||||
cart_items = data.get('cart_items', []) # [{id, name, price, qty, image}]
|
||||
|
||||
# Deduct stock for each sold item
|
||||
@@ -447,7 +448,7 @@ def log_payment():
|
||||
# Update balance atomically with payment log
|
||||
data_manager.set_balance(balance_after)
|
||||
|
||||
if data_manager.log_payment(items, total, balance_before, balance_after):
|
||||
if data_manager.log_payment(items, total, balance_before, balance_after, buyer):
|
||||
return jsonify({'success': True})
|
||||
return jsonify({'success': False, 'message': 'Error logging payment'}), 400
|
||||
except Exception as e:
|
||||
|
||||
@@ -7,5 +7,212 @@
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-02-12T13:34:11.034695"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "test",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:26.155886"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "teste",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:33.908972"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "testeste",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:36.542642"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "testst",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:39.726511"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "etsetset",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:42.367083"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "etsetst",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:44.990438"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "testestest",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:47.949316"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "testestes",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:50.328739"
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "setsettes",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:53.062096"
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "setests",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:55.647214"
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "esttset",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T22:59:58.394528"
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "etestest",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:00.890154"
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "etsetset",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:03.952926"
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "settet",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:07.040332"
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "tetetet",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:09.693125"
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "tetete",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:12.375991"
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "tetetet",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:15.544080"
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "eeee",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:17.805205"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "eeeee",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:39.566745"
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "eeee",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:42.970896"
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "eeeee",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:52.390520"
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "wwww",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:00:55.606973"
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"card_uid": null,
|
||||
"key_uid": null,
|
||||
"name": "ewqewqeqw",
|
||||
"scan_result": "MANUAL",
|
||||
"reason": "Unregistered",
|
||||
"timestamp": "2026-05-30T23:07:00.557343"
|
||||
}
|
||||
]
|
||||
+100
-5
File diff suppressed because one or more lines are too long
+2
-1
@@ -309,7 +309,7 @@ class DataManager:
|
||||
print(f"Error deleting item: {e}")
|
||||
return False
|
||||
|
||||
def log_payment(self, items: list, total: float, balance_before: float, balance_after: float) -> bool:
|
||||
def log_payment(self, items: list, total: float, balance_before: float, balance_after: float, buyer: str = '') -> bool:
|
||||
try:
|
||||
store = self.load_store()
|
||||
payments = store.get('payments', [])
|
||||
@@ -317,6 +317,7 @@ class DataManager:
|
||||
payments.append({
|
||||
'id': new_id,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'buyer': buyer,
|
||||
'items': items,
|
||||
'total': round(total, 2),
|
||||
'balance_before': round(balance_before, 2),
|
||||
|
||||
+596
-420
File diff suppressed because it is too large
Load Diff
+254
-19
@@ -1,6 +1,11 @@
|
||||
let editingParticipantId = null;
|
||||
let isAdmin = false;
|
||||
|
||||
function fmtDate(iso) {
|
||||
const [y, m, d] = iso.split('-');
|
||||
return `${d}-${m}-${y.slice(2)}`;
|
||||
}
|
||||
|
||||
// Initialize on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
checkAdminStatus();
|
||||
@@ -412,8 +417,8 @@ async function refreshLogs() {
|
||||
filteredLogs.forEach(log => {
|
||||
const row = document.createElement('tr');
|
||||
const timestamp = log.timestamp;
|
||||
const date = timestamp.split('T')[0];
|
||||
const time = timestamp.split('T')[1].substring(0, 8);
|
||||
const date = fmtDate(timestamp.split('T')[0]);
|
||||
const time = timestamp.split('T')[1].substring(0, 5);
|
||||
|
||||
// Determine status class
|
||||
let statusClass = 'pass';
|
||||
@@ -728,7 +733,14 @@ function switchTab(tabName, event) {
|
||||
|
||||
// Load tab-specific data
|
||||
if (tabName === 'documents') loadDocuments();
|
||||
if (tabName === 'store') loadStore();
|
||||
if (tabName === 'store') {
|
||||
// Always return to shop view when switching to store tab
|
||||
storeViewIsHistory = false;
|
||||
document.getElementById('storeShopView').style.display = '';
|
||||
document.getElementById('storeHistoryView').style.display = 'none';
|
||||
document.getElementById('storeViewToggleBtn').textContent = 'Zgodovina plačil';
|
||||
loadStore();
|
||||
}
|
||||
}
|
||||
|
||||
// Close modal when clicking outside
|
||||
@@ -746,6 +758,10 @@ window.onclick = function(event) {
|
||||
if (event.target === addUnregisteredModal) closeAddUnregisteredModal();
|
||||
if (event.target === addItemModal) closeAddItemModal();
|
||||
if (event.target === editBalanceModal) closeEditBalanceModal();
|
||||
const checkoutModal = document.getElementById('checkoutModal');
|
||||
const monthlyModal = document.getElementById('monthlyModal');
|
||||
if (event.target === checkoutModal) closeCheckoutModal();
|
||||
if (event.target === monthlyModal) closeMonthlyOverview();
|
||||
}
|
||||
|
||||
// ===== LAST SCAN RESULT =====
|
||||
@@ -909,9 +925,10 @@ function renderInventory() {
|
||||
storeItems.forEach(item => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'inventory-card' + (item.stock === 0 ? ' out-of-stock' : '');
|
||||
const imgHtml = item.image
|
||||
const imgHtml = `<div class="inventory-card-img-wrap">${item.image
|
||||
? `<img src="${item.image}" alt="${item.name}" class="inventory-card-img">`
|
||||
: `<div class="inventory-card-img-placeholder">${ITEM_FALLBACK_SVG}</div>`;
|
||||
: `<div class="inventory-card-img-placeholder">${ITEM_FALLBACK_SVG}</div>`
|
||||
}</div>`;
|
||||
card.innerHTML = `
|
||||
${imgHtml}
|
||||
<div class="inventory-card-body">
|
||||
@@ -1008,16 +1025,40 @@ function updateCartTotals() {
|
||||
}
|
||||
}
|
||||
|
||||
async function processPayment() {
|
||||
function processPayment() {
|
||||
if (cart.length === 0) {
|
||||
alert('Košarica je prazna');
|
||||
return;
|
||||
}
|
||||
|
||||
const total = cart.reduce((sum, i) => sum + i.price * i.qty, 0);
|
||||
const balanceAfter = currentBalance + total;
|
||||
|
||||
if (!confirm(`Skupaj: €${total.toFixed(2)}\nStanje po plačilu: €${balanceAfter.toFixed(2)}\n\nPotrdite plačilo?`)) return;
|
||||
document.getElementById('checkoutFirstName').value = '';
|
||||
document.getElementById('checkoutLastName').value = '';
|
||||
document.getElementById('checkoutTotal').textContent = '€' + total.toFixed(2);
|
||||
document.getElementById('checkoutBalanceAfter').textContent = '€' + balanceAfter.toFixed(2);
|
||||
document.getElementById('checkoutModal').classList.add('show');
|
||||
document.getElementById('checkoutFirstName').focus();
|
||||
}
|
||||
|
||||
function closeCheckoutModal() {
|
||||
document.getElementById('checkoutModal').classList.remove('show');
|
||||
}
|
||||
|
||||
async function confirmCheckout() {
|
||||
const firstName = document.getElementById('checkoutFirstName').value.trim();
|
||||
const lastName = document.getElementById('checkoutLastName').value.trim();
|
||||
|
||||
if (!firstName || !lastName) {
|
||||
alert('Prosimo, vnesite ime in priimek kupca');
|
||||
return;
|
||||
}
|
||||
|
||||
const buyer = firstName + ' ' + lastName;
|
||||
const total = cart.reduce((sum, i) => sum + i.price * i.qty, 0);
|
||||
const balanceAfter = currentBalance + total;
|
||||
|
||||
closeCheckoutModal();
|
||||
|
||||
try {
|
||||
const cartItems = cart.map(cartItem => {
|
||||
@@ -1036,6 +1077,7 @@ async function processPayment() {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
buyer,
|
||||
items: cart.map(i => ({ name: i.name, qty: i.qty, price: i.price })),
|
||||
cart_items: cartItems,
|
||||
total,
|
||||
@@ -1047,9 +1089,18 @@ async function processPayment() {
|
||||
const data = await response.json();
|
||||
if (!data.success) { alert('Napaka: ' + data.message); return; }
|
||||
|
||||
const payment = {
|
||||
buyer,
|
||||
items: cart.map(i => ({ name: i.name, qty: i.qty, price: i.price })),
|
||||
total,
|
||||
balance_before: currentBalance,
|
||||
balance_after: balanceAfter,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
cart = [];
|
||||
await loadStore();
|
||||
alert('Plačilo uspešno!');
|
||||
printReceipt(payment);
|
||||
} catch (error) {
|
||||
alert('Napaka: ' + error.message);
|
||||
}
|
||||
@@ -1212,15 +1263,18 @@ async function saveBalance() {
|
||||
}
|
||||
|
||||
// ===== PAYMENT HISTORY =====
|
||||
let paymentHistoryVisible = false;
|
||||
let storeViewIsHistory = false;
|
||||
|
||||
function togglePaymentHistory() {
|
||||
paymentHistoryVisible = !paymentHistoryVisible;
|
||||
const panel = document.getElementById('paymentHistoryPanel');
|
||||
panel.style.display = paymentHistoryVisible ? 'block' : 'none';
|
||||
if (paymentHistoryVisible) loadPaymentHistory();
|
||||
function toggleStoreView() {
|
||||
storeViewIsHistory = !storeViewIsHistory;
|
||||
document.getElementById('storeShopView').style.display = storeViewIsHistory ? 'none' : '';
|
||||
document.getElementById('storeHistoryView').style.display = storeViewIsHistory ? '' : 'none';
|
||||
document.getElementById('storeViewToggleBtn').textContent = storeViewIsHistory ? '← Nazaj v trgovino' : 'Zgodovina plačil';
|
||||
if (storeViewIsHistory) loadPaymentHistory();
|
||||
}
|
||||
|
||||
function togglePaymentHistory() { toggleStoreView(); }
|
||||
|
||||
async function loadPaymentHistory() {
|
||||
try {
|
||||
const response = await fetch('/api/store/payments');
|
||||
@@ -1232,25 +1286,27 @@ async function loadPaymentHistory() {
|
||||
if (clearBtn) clearBtn.style.display = isAdmin ? 'inline-block' : 'none';
|
||||
|
||||
if (payments.length === 0) {
|
||||
tbody.innerHTML = `<tr><td colspan="7" style="text-align:center;padding:20px;color:var(--text-secondary);">Ni zabeleženih plačil</td></tr>`;
|
||||
tbody.innerHTML = `<tr><td colspan="9" style="text-align:center;padding:20px;color:var(--text-secondary);">Ni zabeleženih plačil</td></tr>`;
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = '';
|
||||
[...payments].reverse().forEach(p => {
|
||||
const ts = p.timestamp;
|
||||
const date = ts.split('T')[0];
|
||||
const time = ts.split('T')[1].substring(0, 8);
|
||||
const date = fmtDate(ts.split('T')[0]);
|
||||
const time = ts.split('T')[1].substring(0, 5);
|
||||
const itemsSummary = p.items.map(i => `${i.name} ×${i.qty}`).join(', ');
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${p.id}</td>
|
||||
<td style="font-weight:600;">${p.buyer || '-'}</td>
|
||||
<td>${date}</td>
|
||||
<td>${time}</td>
|
||||
<td style="max-width:220px;word-break:break-word;">${itemsSummary}</td>
|
||||
<td style="max-width:200px;word-break:break-word;">${itemsSummary}</td>
|
||||
<td style="font-weight:700;color:var(--accent-blue);">€${p.total.toFixed(2)}</td>
|
||||
<td>€${p.balance_before.toFixed(2)}</td>
|
||||
<td>€${p.balance_after.toFixed(2)}</td>
|
||||
<td><button class="action-btn edit-btn" onclick="printReceipt(${JSON.stringify(p).replace(/"/g, '"')})">🖨 Natisni</button></td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
@@ -1259,6 +1315,185 @@ async function loadPaymentHistory() {
|
||||
}
|
||||
}
|
||||
|
||||
// ===== RECEIPT PRINT =====
|
||||
const LOGO_URL = '/static/assets/logo.png';
|
||||
|
||||
function printReceipt(payment) {
|
||||
const ts = payment.timestamp || new Date().toISOString();
|
||||
const date = fmtDate(ts.split('T')[0]);
|
||||
const time = ts.split('T')[1] ? ts.split('T')[1].substring(0, 5) : '';
|
||||
const rows = payment.items.map(i =>
|
||||
`<tr><td class="rp-td-name">${i.name}</td><td class="rp-td-qty">${i.qty}×</td><td class="rp-td-price">€${i.price.toFixed(2)}</td><td class="rp-td-sub">€${(i.price * i.qty).toFixed(2)}</td></tr>`
|
||||
).join('');
|
||||
|
||||
const html = `
|
||||
<div class="rp-page">
|
||||
<div class="rp-logo-block">
|
||||
<img src="${LOGO_URL}" alt="Logo" class="rp-logo" onerror="this.style.display='none'">
|
||||
</div>
|
||||
<div class="rp-divider"></div>
|
||||
<div class="rp-title">POTRDILO O PLAČILU</div>
|
||||
<div class="rp-meta">
|
||||
<span>${date} ${time}</span>
|
||||
<span>Prodajalec: <strong>${payment.buyer || '-'}</strong></span>
|
||||
</div>
|
||||
<div class="rp-divider-dashed"></div>
|
||||
<table class="rp-table">
|
||||
<thead><tr><th>Artikel</th><th>Kol.</th><th>Cena</th><th style="text-align:right;">Znesek</th></tr></thead>
|
||||
<tbody>${rows}</tbody>
|
||||
</table>
|
||||
<div class="rp-divider"></div>
|
||||
<div class="rp-total">
|
||||
<span>SKUPAJ</span>
|
||||
<span>€${payment.total.toFixed(2)}</span>
|
||||
</div>
|
||||
<div class="rp-divider-dashed"></div>
|
||||
<div class="rp-footer">Hvala za nakup!</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const area = document.getElementById('receiptPrintArea');
|
||||
area.innerHTML = html;
|
||||
area.style.display = 'block';
|
||||
document.body.classList.add('printing-receipt');
|
||||
window.print();
|
||||
document.body.classList.remove('printing-receipt');
|
||||
area.style.display = 'none';
|
||||
}
|
||||
|
||||
// ===== MONTHLY OVERVIEW =====
|
||||
let allPaymentsCache = [];
|
||||
|
||||
function openMonthlyOverview() {
|
||||
const now = new Date();
|
||||
const ym = now.toISOString().substring(0, 7);
|
||||
document.getElementById('monthlyPickerInput').value = ym;
|
||||
document.getElementById('monthlyModal').classList.add('show');
|
||||
renderMonthlyOverview();
|
||||
}
|
||||
|
||||
function closeMonthlyOverview() {
|
||||
document.getElementById('monthlyModal').classList.remove('show');
|
||||
}
|
||||
|
||||
async function renderMonthlyOverview() {
|
||||
const picker = document.getElementById('monthlyPickerInput').value;
|
||||
if (!picker) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/store/payments');
|
||||
allPaymentsCache = await response.json();
|
||||
} catch (e) { return; }
|
||||
|
||||
const filtered = allPaymentsCache.filter(p => p.timestamp.startsWith(picker));
|
||||
const content = document.getElementById('monthlyOverviewContent');
|
||||
|
||||
if (filtered.length === 0) {
|
||||
content.innerHTML = '<p style="color:var(--text-secondary);padding:16px 0;">Ni plačil v izbranem mesecu.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
const grandTotal = filtered.reduce((s, p) => s + p.total, 0);
|
||||
|
||||
// Per-item breakdown
|
||||
const itemTotals = {};
|
||||
filtered.forEach(p => p.items.forEach(i => {
|
||||
if (!itemTotals[i.name]) itemTotals[i.name] = { qty: 0, revenue: 0 };
|
||||
itemTotals[i.name].qty += i.qty;
|
||||
itemTotals[i.name].revenue += i.price * i.qty;
|
||||
}));
|
||||
|
||||
const itemRows = Object.entries(itemTotals).map(([name, v]) =>
|
||||
`<tr><td>${name}</td><td style="text-align:center;">${v.qty}</td><td style="text-align:right;font-weight:600;">€${v.revenue.toFixed(2)}</td></tr>`
|
||||
).join('');
|
||||
|
||||
const txRows = [...filtered].reverse().map(p => {
|
||||
const date = fmtDate(p.timestamp.split('T')[0]);
|
||||
const time = p.timestamp.split('T')[1].substring(0, 5);
|
||||
return `<tr><td>${date}</td><td>${time}</td><td style="font-weight:600;">${p.buyer || '-'}</td><td>${p.items.map(i => `${i.name} ×${i.qty}`).join(', ')}</td><td style="text-align:right;font-weight:700;color:var(--accent-blue);">€${p.total.toFixed(2)}</td></tr>`;
|
||||
}).join('');
|
||||
|
||||
content.innerHTML = `
|
||||
<div class="monthly-summary-total">Skupaj prihodek: <strong>€${grandTotal.toFixed(2)}</strong> (${filtered.length} transakcij)</div>
|
||||
<h4 class="monthly-section-title">Po artiklih</h4>
|
||||
<div class="table-container" style="margin-bottom:16px;">
|
||||
<table><thead><tr><th>Artikel</th><th style="text-align:center;">Količina</th><th style="text-align:right;">Prihodek</th></tr></thead>
|
||||
<tbody>${itemRows}</tbody></table>
|
||||
</div>
|
||||
<h4 class="monthly-section-title">Vse transakcije</h4>
|
||||
<div class="table-container">
|
||||
<table><thead><tr><th>Datum</th><th>Čas</th><th>Prodajalec</th><th>Artikli</th><th style="text-align:right;">Skupaj</th></tr></thead>
|
||||
<tbody>${txRows}</tbody></table>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function printMonthlyOverview() {
|
||||
const picker = document.getElementById('monthlyPickerInput').value;
|
||||
if (!picker || !allPaymentsCache.length) return;
|
||||
|
||||
const filtered = allPaymentsCache.filter(p => p.timestamp.startsWith(picker));
|
||||
if (filtered.length === 0) { alert('Ni podatkov za izbrani mesec.'); return; }
|
||||
|
||||
const grandTotal = filtered.reduce((s, p) => s + p.total, 0);
|
||||
|
||||
const itemTotals = {};
|
||||
filtered.forEach(p => p.items.forEach(i => {
|
||||
if (!itemTotals[i.name]) itemTotals[i.name] = { qty: 0, revenue: 0 };
|
||||
itemTotals[i.name].qty += i.qty;
|
||||
itemTotals[i.name].revenue += i.price * i.qty;
|
||||
}));
|
||||
|
||||
const itemRows = Object.entries(itemTotals).map(([name, v]) =>
|
||||
`<tr><td>${name}</td><td style="text-align:center;">${v.qty}</td><td style="text-align:right;font-weight:600;">€${v.revenue.toFixed(2)}</td></tr>`
|
||||
).join('');
|
||||
|
||||
const txRows = [...filtered].reverse().map(p => {
|
||||
const date = fmtDate(p.timestamp.split('T')[0]);
|
||||
const time = p.timestamp.split('T')[1].substring(0, 5);
|
||||
const items = p.items.map(i => `${i.name} ×${i.qty} (€${(i.price*i.qty).toFixed(2)})`).join(', ');
|
||||
return `<tr><td>${date}</td><td>${time}</td><td style="font-weight:600;">${p.buyer || '-'}</td><td>${items}</td><td style="text-align:right;font-weight:700;">€${p.total.toFixed(2)}</td></tr>`;
|
||||
}).join('');
|
||||
|
||||
const [year, month] = picker.split('-');
|
||||
const monthName = new Date(year, month - 1, 1).toLocaleDateString('sl-SI', { month: 'long', year: 'numeric' });
|
||||
const printDate = fmtDate(new Date().toISOString().split('T')[0]);
|
||||
|
||||
const html = `
|
||||
<div class="rp-page rp-monthly">
|
||||
<div class="rp-logo-block">
|
||||
<img src="${LOGO_URL}" alt="Logo" class="rp-logo" onerror="this.style.display='none'">
|
||||
</div>
|
||||
<div class="rp-divider"></div>
|
||||
<div class="rp-title">MESEČNI PREGLED</div>
|
||||
<div class="rp-meta"><span>${monthName}</span><span>Natisnjeno: ${printDate}</span></div>
|
||||
<div class="rp-divider-dashed"></div>
|
||||
<div class="rp-monthly-kpi">
|
||||
<div class="rp-kpi-box"><div class="rp-kpi-label">Skupaj prihodek</div><div class="rp-kpi-value">€${grandTotal.toFixed(2)}</div></div>
|
||||
<div class="rp-kpi-box"><div class="rp-kpi-label">Št. transakcij</div><div class="rp-kpi-value">${filtered.length}</div></div>
|
||||
</div>
|
||||
<div class="rp-section-title">Po artiklih</div>
|
||||
<table class="rp-table">
|
||||
<thead><tr><th>Artikel</th><th style="text-align:center;">Količina</th><th style="text-align:right;">Prihodek</th></tr></thead>
|
||||
<tbody>${itemRows}</tbody>
|
||||
</table>
|
||||
<div class="rp-section-title" style="margin-top:16px;">Vse transakcije</div>
|
||||
<table class="rp-table">
|
||||
<thead><tr><th>Datum</th><th>Čas</th><th>Prodajalec</th><th>Artikli</th><th style="text-align:right;">Skupaj</th></tr></thead>
|
||||
<tbody>${txRows}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const area = document.getElementById('receiptPrintArea');
|
||||
area.innerHTML = html;
|
||||
area.style.display = 'block';
|
||||
document.body.classList.add('printing-receipt');
|
||||
window.print();
|
||||
document.body.classList.remove('printing-receipt');
|
||||
area.style.display = 'none';
|
||||
}
|
||||
|
||||
async function clearPaymentHistory() {
|
||||
if (!confirm('Izbriši celotno zgodovino plačil?')) return;
|
||||
try {
|
||||
|
||||
+104
-67
@@ -52,7 +52,7 @@
|
||||
<div class="right-panel">
|
||||
<!-- Tab Navigation -->
|
||||
<div class="tab-nav">
|
||||
<button class="tab-nav-btn active" onclick="switchTab('logs', event)">Dnevnik skeniranja</button>
|
||||
<button class="tab-nav-btn active" onclick="switchTab('logs', event)">Dnevnik</button>
|
||||
<button class="tab-nav-btn" onclick="switchTab('participants', event)">Člani</button>
|
||||
<button class="tab-nav-btn" onclick="switchTab('documents', event)">Dokumenti</button>
|
||||
<button class="tab-nav-btn" onclick="switchTab('store', event)">Trgovina</button>
|
||||
@@ -60,9 +60,11 @@
|
||||
|
||||
<!-- Participants Tab -->
|
||||
<div id="participants-tab" class="tab-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Člani</h2>
|
||||
<button class="btn btn-primary" onclick="openAddParticipantModal()">Dodaj udeleženca</button>
|
||||
<div class="toolbar">
|
||||
<span class="toolbar-title">Člani</span>
|
||||
<div class="toolbar-actions">
|
||||
<button class="tb-btn tb-btn-primary" onclick="openAddParticipantModal()">+ Dodaj udeleženca</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<div class="table-container">
|
||||
@@ -78,8 +80,7 @@
|
||||
<th>Dejanja</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="participantsTable">
|
||||
</tbody>
|
||||
<tbody id="participantsTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -87,49 +88,43 @@
|
||||
|
||||
<!-- Scan Logs Tab -->
|
||||
<div id="logs-tab" class="tab-panel active">
|
||||
<div class="panel-header">
|
||||
<h2>Zgodovina skeniranja</h2>
|
||||
<div class="header-actions">
|
||||
<button class="btn btn-primary" onclick="exportLogs()">Izvozi v Excel</button>
|
||||
<button class="btn btn-primary" onclick="printLogs()">Natisni</button>
|
||||
<button id="addUnregisteredBtn" class="btn btn-primary" onclick="openAddUnregisteredModal()">Dodaj neregistriranega</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-filters">
|
||||
<div class="filters-row">
|
||||
<div class="filter-group">
|
||||
<label>Rezultat:</label>
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-filters">
|
||||
<div class="tf-group">
|
||||
<label>Rezultat</label>
|
||||
<select id="filterSelect" onchange="refreshLogs()">
|
||||
<option value="All">Vse</option>
|
||||
<option value="PASS">Samo PASS</option>
|
||||
<option value="FAIL">Samo FAIL</option>
|
||||
<option value="PASS">PASS</option>
|
||||
<option value="FAIL">FAIL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label>Udeleženec:</label>
|
||||
<div class="tf-group">
|
||||
<label>Udeleženec</label>
|
||||
<select id="participantFilter" onchange="refreshLogs()">
|
||||
<option value="">Vsi udeleženci</option>
|
||||
<option value="">Vsi</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label>Od:</label>
|
||||
<div class="tf-group">
|
||||
<label>Od</label>
|
||||
<input type="date" id="startDateFilter" onchange="refreshLogs()">
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label>Do:</label>
|
||||
<div class="tf-group">
|
||||
<label>Do</label>
|
||||
<input type="date" id="endDateFilter" onchange="refreshLogs()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="filters-quick">
|
||||
<div class="tf-quick">
|
||||
<button class="btn-quick-filter" onclick="filterByToday()">Danes</button>
|
||||
<button class="btn-quick-filter" onclick="filterByLastWeek()">Teden</button>
|
||||
<button class="btn-quick-filter" onclick="filterByLastMonth()">Mesec</button>
|
||||
<button class="btn-quick-filter" onclick="filterByLastYear()">Leto</button>
|
||||
<button class="btn-filter" onclick="resetFilters()">Resetiraj</button>
|
||||
<button class="btn-filter" onclick="resetFilters()">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<button class="tb-btn" onclick="printLogs()">Natisni</button>
|
||||
<button id="addUnregisteredBtn" class="tb-btn tb-btn-primary" onclick="openAddUnregisteredModal()">+ Neregistriran</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel-content">
|
||||
<div class="table-container">
|
||||
<table>
|
||||
@@ -145,8 +140,7 @@
|
||||
<th id="logs-actions-header">Dejanja</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="logsTable">
|
||||
</tbody>
|
||||
<tbody id="logsTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,8 +148,8 @@
|
||||
|
||||
<!-- Documents Tab -->
|
||||
<div id="documents-tab" class="tab-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Dokumenti</h2>
|
||||
<div class="toolbar">
|
||||
<span class="toolbar-title">Dokumenti</span>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<div id="documentsGrid" class="documents-grid">
|
||||
@@ -166,35 +160,32 @@
|
||||
|
||||
<!-- Store Tab -->
|
||||
<div id="store-tab" class="tab-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Trgovina</h2>
|
||||
<div class="header-actions">
|
||||
<div class="store-balance-display">
|
||||
<span class="store-balance-label">Stanje:</span>
|
||||
<!-- Single shared toolbar -->
|
||||
<div class="toolbar">
|
||||
<span class="toolbar-title">Trgovina</span>
|
||||
<div class="toolbar-actions">
|
||||
<div class="store-balance-chip">
|
||||
<span class="store-balance-label">Stanje</span>
|
||||
<span id="storeBalanceDisplay" class="store-balance-value">€0.00</span>
|
||||
<button id="editBalanceBtn" class="btn btn-primary" onclick="openEditBalanceModal()" style="display:none;">Uredi stanje</button>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="togglePaymentHistory()">Zgodovina plačil</button>
|
||||
<button id="editBalanceBtn" class="tb-btn" onclick="openEditBalanceModal()" style="display:none;">Uredi stanje</button>
|
||||
<button id="addItemBtn" class="tb-btn tb-btn-primary" onclick="openAddItemModal()" style="display:none;">+ Artikel</button>
|
||||
<button id="storeViewToggleBtn" class="tb-btn" onclick="toggleStoreView()">Zgodovina plačil</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="store-tab-scroll panel-content">
|
||||
|
||||
<!-- Shop view -->
|
||||
<div id="storeShopView" class="store-tab-scroll panel-content">
|
||||
<div class="store-layout">
|
||||
<!-- Inventory -->
|
||||
<div class="store-inventory">
|
||||
<div class="store-section-header">
|
||||
<h3>Zaloga</h3>
|
||||
<button id="addItemBtn" class="btn btn-primary" onclick="openAddItemModal()" style="display:none;">+ Dodaj artikel</button>
|
||||
</div>
|
||||
<div class="inventory-grid" id="inventoryGrid">
|
||||
<p style="color: var(--text-secondary);">Nalaganje...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cart -->
|
||||
<div class="store-cart">
|
||||
<div class="store-section-header">
|
||||
<h3>Košarica</h3>
|
||||
<button class="btn btn-primary" onclick="clearCart()">Počisti</button>
|
||||
<div class="store-cart-header">
|
||||
<span class="store-cart-title">Košarica</span>
|
||||
<button class="tb-btn" onclick="clearCart()">Počisti</button>
|
||||
</div>
|
||||
<div class="cart-items" id="cartItems">
|
||||
<p style="color: var(--text-secondary); text-align: center; padding: 20px;">Košarica je prazna</p>
|
||||
@@ -212,36 +203,34 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- end store-layout -->
|
||||
|
||||
<!-- Payment History Panel -->
|
||||
<div id="paymentHistoryPanel" class="payment-history-panel" style="display:none;">
|
||||
<div class="store-section-header" style="padding: 0 16px 12px;">
|
||||
<h3>Zgodovina plačil</h3>
|
||||
<button id="clearHistoryBtn" class="action-btn delete-btn" onclick="clearPaymentHistory()" style="display:none;">Izbriši zgodovino</button>
|
||||
</div>
|
||||
<div class="table-container" style="margin: 0 16px 16px;">
|
||||
|
||||
<!-- History view -->
|
||||
<div id="storeHistoryView" class="panel-content" style="display:none;">
|
||||
<div class="history-toolbar">
|
||||
<button class="tb-btn" onclick="openMonthlyOverview()">Mesečni pregled</button>
|
||||
<button id="clearHistoryBtn" class="tb-btn" onclick="clearPaymentHistory()" style="display:none;color:var(--accent-red);border-color:#fca5a5;">Izbriši zgodovino</button>
|
||||
</div>
|
||||
<div class="table-container" style="height:calc(100% - 46px);">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Prodajalec</th>
|
||||
<th>Datum</th>
|
||||
<th>Čas</th>
|
||||
<th>Artikli</th>
|
||||
<th>Skupaj</th>
|
||||
<th>Stanje pred</th>
|
||||
<th>Stanje po</th>
|
||||
<th>Potrdilo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="paymentHistoryTable">
|
||||
</tbody>
|
||||
<tbody id="paymentHistoryTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Item Modal -->
|
||||
<div id="addItemModal" class="modal">
|
||||
@@ -367,6 +356,54 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Checkout Modal -->
|
||||
<div id="checkoutModal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close" onclick="closeCheckoutModal()">×</span>
|
||||
<h2>Podatki prodajalca</h2>
|
||||
<div class="form-group">
|
||||
<label>Ime:</label>
|
||||
<input type="text" id="checkoutFirstName" placeholder="Ime">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Priimek:</label>
|
||||
<input type="text" id="checkoutLastName" placeholder="Priimek" onkeydown="if(event.key==='Enter')confirmCheckout()">
|
||||
</div>
|
||||
<div class="checkout-summary">
|
||||
<div class="checkout-summary-row">
|
||||
<span>Skupaj za plačilo:</span>
|
||||
<span id="checkoutTotal" class="checkout-total-val"></span>
|
||||
</div>
|
||||
<div class="checkout-summary-row">
|
||||
<span>Stanje po plačilu:</span>
|
||||
<span id="checkoutBalanceAfter" class="checkout-balance-val"></span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="confirmCheckout()">Potrdi plačilo</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Monthly Overview Modal -->
|
||||
<div id="monthlyModal" class="modal">
|
||||
<div class="modal-content modal-content-wide">
|
||||
<div class="monthly-modal-header">
|
||||
<h2>Mesečni pregled</h2>
|
||||
<span class="close" onclick="closeMonthlyOverview()">×</span>
|
||||
</div>
|
||||
<div class="monthly-modal-toolbar">
|
||||
<div class="filter-group" style="flex:0 0 auto;min-width:180px;">
|
||||
<label>Mesec:</label>
|
||||
<input type="month" id="monthlyPickerInput" onchange="renderMonthlyOverview()">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="printMonthlyOverview()" style="width:auto;padding:10px 18px;margin:0;font-size:12px;flex-shrink:0;">Natisni pregled</button>
|
||||
</div>
|
||||
<div id="monthlyOverviewContent" class="monthly-modal-body"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden receipt for printing -->
|
||||
<div id="receiptPrintArea" style="display:none;"></div>
|
||||
|
||||
<div id="print-header" style="visibility:hidden;position:fixed;">
|
||||
<img src="{{ url_for('static', filename='assets/logo.png') }}" alt="Logo">
|
||||
<p id="print-date-range" style="font-size:13px;color:#444;margin-top:4px;"></p>
|
||||
|
||||
Reference in New Issue
Block a user