Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -41,9 +41,9 @@ DATA_FILES = {
|
|
| 41 |
|
| 42 |
HF_TOKEN_WRITE = os.getenv("HF_TOKEN_WRITE", "YOUR_WRITE_TOKEN_HERE")
|
| 43 |
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ", "YOUR_READ_TOKEN_HERE")
|
| 44 |
-
REPO_ID = "Kgshop/
|
| 45 |
|
| 46 |
-
BISHKEK_TZ = pytz.timezone('Asia/
|
| 47 |
|
| 48 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 49 |
|
|
@@ -198,7 +198,7 @@ def generate_receipt_html(transaction):
|
|
| 198 |
<tbody>{items_html}</tbody>
|
| 199 |
</table>
|
| 200 |
<p>--------------------------------</p>
|
| 201 |
-
<p class="total">Итого: {format_currency_py(transaction['total_amount'])}
|
| 202 |
<p>Способ оплаты: {'Наличные' if transaction['payment_method'] == 'cash' else 'Карта'}</p>
|
| 203 |
<p>--------------------------------</p>
|
| 204 |
<p>Спасибо за покупку!</p>
|
|
@@ -1125,11 +1125,8 @@ BASE_TEMPLATE = """
|
|
| 1125 |
<p>Чек успешно создан.</p>
|
| 1126 |
<div class="mb-3">
|
| 1127 |
<label for="whatsapp-phone" class="form-label">Отправить чек на WhatsApp</label>
|
| 1128 |
-
<
|
| 1129 |
-
|
| 1130 |
-
<input type="tel" class="form-control" id="whatsapp-phone" placeholder="555123456">
|
| 1131 |
-
</div>
|
| 1132 |
-
<div class="form-text">Введите номер без +996.</div>
|
| 1133 |
</div>
|
| 1134 |
<input type="hidden" id="receipt-url">
|
| 1135 |
</div>
|
|
@@ -1203,9 +1200,9 @@ SALES_SCREEN_CONTENT = """
|
|
| 1203 |
<h6 class="card-title small mb-1">{{ p.name }}</h6>
|
| 1204 |
<p class="card-text fw-bold mb-0">
|
| 1205 |
{% if p.variants|length > 1 %}
|
| 1206 |
-
от {{ format_currency_py(p.variants|map(attribute='price')|min) }}
|
| 1207 |
{% elif p.variants|length == 1 %}
|
| 1208 |
-
{{ format_currency_py(p.variants[0].price) }}
|
| 1209 |
{% else %}
|
| 1210 |
Нет в наличии
|
| 1211 |
{% endif %}
|
|
@@ -1228,7 +1225,7 @@ SALES_SCREEN_CONTENT = """
|
|
| 1228 |
<div id="cart-items" class="list-group mb-3"></div>
|
| 1229 |
<div class="d-flex justify-content-between align-items-center mb-3">
|
| 1230 |
<h4 class="mb-0">Итого:</h4>
|
| 1231 |
-
<h4 class="mb-0" id="cart-total">0,00
|
| 1232 |
</div>
|
| 1233 |
<div class="d-grid gap-2">
|
| 1234 |
<div class="btn-group"><button class="btn btn-success flex-grow-1" id="pay-cash-btn"><i class="fas fa-money-bill-wave me-2"></i>Наличные</button><button class="btn btn-info flex-grow-1" id="pay-card-btn"><i class="far fa-credit-card me-2"></i>Карта</button></div>
|
|
@@ -1282,7 +1279,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1282 |
<div class="list-group-item d-flex justify-content-between align-items-center">
|
| 1283 |
<div>
|
| 1284 |
<h6 class="mb-0 small">${item.productName} (${item.variantName})</h6>
|
| 1285 |
-
<small>${item.price}
|
| 1286 |
</div>
|
| 1287 |
<div class="d-flex align-items-center">
|
| 1288 |
<button class="btn btn-sm btn-outline-secondary cart-qty-btn" data-id="${id}" data-op="-1">-</button>
|
|
@@ -1291,7 +1288,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1291 |
</div>
|
| 1292 |
</div>`;
|
| 1293 |
}
|
| 1294 |
-
cartTotalEl.textContent = total.toLocaleString('ru-RU', {minimumFractionDigits: 2, maximumFractionDigits: 2}) + '
|
| 1295 |
};
|
| 1296 |
|
| 1297 |
const addToCart = (product, variant) => {
|
|
@@ -1325,7 +1322,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1325 |
const btn = document.createElement('button');
|
| 1326 |
btn.type = 'button';
|
| 1327 |
btn.className = 'list-group-item list-group-item-action';
|
| 1328 |
-
btn.innerHTML = `${variant.option_value} - <strong>${parseFloat(variant.price).toLocaleString('ru-RU', {minimumFractionDigits: 2})}
|
| 1329 |
btn.addEventListener('click', () => {
|
| 1330 |
addToCart(product, variant);
|
| 1331 |
variantSelectModal.hide();
|
|
@@ -1434,7 +1431,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
| 1434 |
const phone = document.getElementById('whatsapp-phone').value.replace(/\\D/g, '');
|
| 1435 |
const receiptUrl = document.getElementById('receipt-url').value;
|
| 1436 |
if (phone && receiptUrl) {
|
| 1437 |
-
const fullPhone =
|
| 1438 |
const message = encodeURIComponent(`Ваш чек: ${receiptUrl}`);
|
| 1439 |
window.open(`https://wa.me/${fullPhone}?text=${message}`, '_blank');
|
| 1440 |
} else {
|
|
@@ -1514,8 +1511,8 @@ INVENTORY_CONTENT = """
|
|
| 1514 |
{% for v in p.variants %}
|
| 1515 |
<tr>
|
| 1516 |
<td>{{ v.option_value }}</td>
|
| 1517 |
-
<td>{{ format_currency_py(v.price) }}
|
| 1518 |
-
<td>{{ format_currency_py(v.cost_price) }}
|
| 1519 |
<td>{{ v.stock }}</td>
|
| 1520 |
</tr>
|
| 1521 |
{% else %}
|
|
@@ -1751,7 +1748,7 @@ TRANSACTIONS_CONTENT = """
|
|
| 1751 |
<td>{{ t.timestamp[:16]|replace('T', ' ') }}</td>
|
| 1752 |
<td><span class="badge bg-{{'primary' if t.type == 'sale' else 'warning'}}">{{'Продажа' if t.type == 'sale' else 'Возврат'}}</span></td>
|
| 1753 |
<td>{{ t.user_name }}</td><td>{{ t.kassa_name }}</td>
|
| 1754 |
-
<td class="fw-bold">{{ format_currency_py(t.total_amount) }}
|
| 1755 |
<td><span class="badge bg-{{'success' if t.status == 'completed' else 'secondary'}}">{{t.status}}</span></td>
|
| 1756 |
<td>
|
| 1757 |
<ul class="list-unstyled mb-0 small">
|
|
@@ -1788,12 +1785,12 @@ REPORTS_CONTENT = """
|
|
| 1788 |
<div class="card-header"><h5 class="mb-0">Сводный отчет за период</h5></div>
|
| 1789 |
<div class="card-body">
|
| 1790 |
<ul class="list-group list-group-flush">
|
| 1791 |
-
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-chart-bar me-2 text-primary"></i>Выручка (за вычетом возвратов)</span> <strong>{{ format_currency_py(stats.total_revenue) }}
|
| 1792 |
-
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-cogs me-2 text-secondary"></i>Себестоимость проданных товаров</span> <strong>{{ format_currency_py(stats.total_cogs) }}
|
| 1793 |
-
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-piggy-bank me-2 text-info"></i>Валовая прибыль</span> <strong class="text-info">{{ format_currency_py(stats.gross_profit) }}
|
| 1794 |
-
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-receipt me-2 text-warning"></i>Расходы (операционные)</span> <strong class="text-warning">-{{ format_currency_py(stats.total_expenses) }}
|
| 1795 |
-
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-users-cog me-2 text-danger"></i>Расходы (зарплаты)</span> <strong class="text-danger">-{{ format_currency_py(stats.total_salary_expenses) }}
|
| 1796 |
-
<li class="list-group-item d-flex justify-content-between fs-5"><span><i class="fas fa-check-circle me-2 text-success"></i>Чистая прибыль</span> <strong class="text-success">{{ format_currency_py(stats.net_profit) }}
|
| 1797 |
</ul>
|
| 1798 |
</div>
|
| 1799 |
</div>
|
|
@@ -1804,7 +1801,7 @@ REPORTS_CONTENT = """
|
|
| 1804 |
<thead><tr><th>Кассир</th><th class="text-end">К выплате</th></tr></thead>
|
| 1805 |
<tbody>
|
| 1806 |
{% for name, payout in stats.cashier_payouts %}
|
| 1807 |
-
<tr><td>{{ name }}</td><td class="text-end">{{ format_currency_py(payout) }}
|
| 1808 |
{% else %}<tr><td colspan="2" class="text-center text-muted">Нет данных для расчета.</td></tr>
|
| 1809 |
{% endfor %}
|
| 1810 |
</tbody>
|
|
@@ -1820,7 +1817,7 @@ REPORTS_CONTENT = """
|
|
| 1820 |
<thead><tr><th>Кассир</th><th>Чеков</th><th class="text-end">Сумма</th></tr></thead>
|
| 1821 |
<tbody>
|
| 1822 |
{% for name, data in stats.sales_by_cashier %}
|
| 1823 |
-
<tr><td>{{ name }}</td><td>{{ data.count }}</td><td class="text-end">{{ format_currency_py(data.total) }}
|
| 1824 |
{% else %}<tr><td colspan="3" class="text-center text-muted">Нет продаж за выбранный период.</td></tr>
|
| 1825 |
{% endfor %}
|
| 1826 |
</tbody>
|
|
@@ -1837,7 +1834,7 @@ REPORTS_CONTENT = """
|
|
| 1837 |
<tr>
|
| 1838 |
<td><small>{{ expense.timestamp[:10] }}</small></td>
|
| 1839 |
<td>{{ expense.description }}</td>
|
| 1840 |
-
<td class="text-end">{{ format_currency_py(expense.amount) }}
|
| 1841 |
</tr>
|
| 1842 |
{% else %}<tr><td colspan="3" class="text-center text-muted">Нет расходов за выбранный период.</td></tr>
|
| 1843 |
{% endfor %}
|
|
@@ -1872,14 +1869,14 @@ PRODUCT_ROI_CONTENT = """
|
|
| 1872 |
<tr>
|
| 1873 |
<td><strong>{{ item.name }}</strong><br><small class="text-muted">{{ item.variant_name }}</small></td>
|
| 1874 |
<td class="text-end">{{ item.total_qty_sold }}</td>
|
| 1875 |
-
<td class="text-end">{{ format_currency_py(item.total_revenue) }}
|
| 1876 |
-
<td class="text-end">{{ format_currency_py(item.inventory_value) }}
|
| 1877 |
-
<td class="text-end text-secondary">{{ format_currency_py(item.total_investment) }}
|
| 1878 |
<td class="text-end fw-bold
|
| 1879 |
{% if item.payback > 0 %}payback-positive
|
| 1880 |
{% elif item.payback < 0 %}payback-negative
|
| 1881 |
{% else %}payback-zero{% endif %}">
|
| 1882 |
-
{{ format_currency_py(item.payback) }}
|
| 1883 |
</td>
|
| 1884 |
</tr>
|
| 1885 |
{% else %}
|
|
@@ -1908,7 +1905,7 @@ ADMIN_CONTENT = """
|
|
| 1908 |
<li class="list-group-item d-flex justify-content-between align-items-center">
|
| 1909 |
<div>{{ u.name }} <br>
|
| 1910 |
<small class="text-muted">
|
| 1911 |
-
{% if u.payment_type == 'salary' %}Зарплата: {{ format_currency_py(u.payment_value) }}
|
| 1912 |
{% elif u.payment_type == 'percentage' %}Процент: {{ u.payment_value }}%
|
| 1913 |
{% endif %}
|
| 1914 |
</small>
|
|
@@ -1938,7 +1935,7 @@ ADMIN_CONTENT = """
|
|
| 1938 |
<ul class="list-group">
|
| 1939 |
{% for k in kassas %}
|
| 1940 |
<li class="list-group-item d-flex justify-content-between align-items-center">
|
| 1941 |
-
<div>{{ k.name }} <br><small class="fw-bold">{{ format_currency_py(k.balance) }}
|
| 1942 |
<form action="{{ url_for('manage_kassa') }}" method="POST" onsubmit="return confirm('Удалить кассу?');"><input type="hidden" name="action" value="delete"><input type="hidden" name="id" value="{{ k.id }}"><button type="submit" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i></button></form>
|
| 1943 |
</li>
|
| 1944 |
{% endfor %}
|
|
@@ -1981,7 +1978,7 @@ ADMIN_CONTENT = """
|
|
| 1981 |
<tr>
|
| 1982 |
<td><small>{{ e.timestamp[:16]|replace('T', ' ') }}</small></td>
|
| 1983 |
<td>{{ e.description }}</td>
|
| 1984 |
-
<td class="text-end fw-bold">{{ format_currency_py(e.amount) }}
|
| 1985 |
</tr>
|
| 1986 |
{% else %}
|
| 1987 |
<tr><td colspan="3" class="text-center">Расходов пока нет</td></tr>
|
|
@@ -2018,7 +2015,7 @@ ADMIN_CONTENT = """
|
|
| 2018 |
<div class="mb-3"><label class="form-label">Имя</label><input type="text" name="name" class="form-control" required></div>
|
| 2019 |
<div class="mb-3"><label class="form-label">ПИН-код</label><input type="password" name="pin" class="form-control" required></div>
|
| 2020 |
<div class="mb-3"><label class="form-label">Тип оплаты</label><select name="payment_type" class="form-select"><option value="percentage">Процент от продаж</option><option value="salary">Фиксированная зарплата</option></select></div>
|
| 2021 |
-
<div class="mb-3"><label class="form-label">Значение</label><input type="text" name="payment_value" class="form-control" inputmode="decimal" value="0" required><small class="form-text text-muted">Для процентов - число (напр. 5), для зарплаты - сумма в
|
| 2022 |
</div>
|
| 2023 |
<div class="modal-footer"><button type="submit" class="btn btn-primary">Сохранить</button></div>
|
| 2024 |
</form>
|
|
@@ -2106,7 +2103,7 @@ CASHIER_DASHBOARD_CONTENT = """
|
|
| 2106 |
<td><small class="text-muted">{{ t.id[:8] }}</small></td>
|
| 2107 |
<td>{{ t.timestamp[:16]|replace('T', ' ') }}</td>
|
| 2108 |
<td><span class="badge bg-{{'primary' if t.type == 'sale' else 'warning'}}">{{'Продажа' if t.type == 'sale' else 'Возврат'}}</span></td>
|
| 2109 |
-
<td class="fw-bold">{{ format_currency_py(t.total_amount) }}
|
| 2110 |
<td><span class="badge bg-{{'success' if t.status == 'completed' else 'secondary'}}">{{t.status}}</span></td>
|
| 2111 |
<td>
|
| 2112 |
{% if t.type == 'sale' and t.status == 'completed' %}
|
|
|
|
| 41 |
|
| 42 |
HF_TOKEN_WRITE = os.getenv("HF_TOKEN_WRITE", "YOUR_WRITE_TOKEN_HERE")
|
| 43 |
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ", "YOUR_READ_TOKEN_HERE")
|
| 44 |
+
REPO_ID = "Kgshop/morshenbasekz"
|
| 45 |
|
| 46 |
+
BISHKEK_TZ = pytz.timezone('Asia/Almaty')
|
| 47 |
|
| 48 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 49 |
|
|
|
|
| 198 |
<tbody>{items_html}</tbody>
|
| 199 |
</table>
|
| 200 |
<p>--------------------------------</p>
|
| 201 |
+
<p class="total">Итого: {format_currency_py(transaction['total_amount'])} ₸</p>
|
| 202 |
<p>Способ оплаты: {'Наличные' if transaction['payment_method'] == 'cash' else 'Карта'}</p>
|
| 203 |
<p>--------------------------------</p>
|
| 204 |
<p>Спасибо за покупку!</p>
|
|
|
|
| 1125 |
<p>Чек успешно создан.</p>
|
| 1126 |
<div class="mb-3">
|
| 1127 |
<label for="whatsapp-phone" class="form-label">Отправить чек на WhatsApp</label>
|
| 1128 |
+
<input type="tel" class="form-control" id="whatsapp-phone" placeholder="996555123456">
|
| 1129 |
+
<div class="form-text">Введите полный номер с кодом страны (без +).</div>
|
|
|
|
|
|
|
|
|
|
| 1130 |
</div>
|
| 1131 |
<input type="hidden" id="receipt-url">
|
| 1132 |
</div>
|
|
|
|
| 1200 |
<h6 class="card-title small mb-1">{{ p.name }}</h6>
|
| 1201 |
<p class="card-text fw-bold mb-0">
|
| 1202 |
{% if p.variants|length > 1 %}
|
| 1203 |
+
от {{ format_currency_py(p.variants|map(attribute='price')|min) }} ₸
|
| 1204 |
{% elif p.variants|length == 1 %}
|
| 1205 |
+
{{ format_currency_py(p.variants[0].price) }} ₸
|
| 1206 |
{% else %}
|
| 1207 |
Нет в наличии
|
| 1208 |
{% endif %}
|
|
|
|
| 1225 |
<div id="cart-items" class="list-group mb-3"></div>
|
| 1226 |
<div class="d-flex justify-content-between align-items-center mb-3">
|
| 1227 |
<h4 class="mb-0">Итого:</h4>
|
| 1228 |
+
<h4 class="mb-0" id="cart-total">0,00 ₸</h4>
|
| 1229 |
</div>
|
| 1230 |
<div class="d-grid gap-2">
|
| 1231 |
<div class="btn-group"><button class="btn btn-success flex-grow-1" id="pay-cash-btn"><i class="fas fa-money-bill-wave me-2"></i>Наличные</button><button class="btn btn-info flex-grow-1" id="pay-card-btn"><i class="far fa-credit-card me-2"></i>Карта</button></div>
|
|
|
|
| 1279 |
<div class="list-group-item d-flex justify-content-between align-items-center">
|
| 1280 |
<div>
|
| 1281 |
<h6 class="mb-0 small">${item.productName} (${item.variantName})</h6>
|
| 1282 |
+
<small>${item.price} ₸</small>
|
| 1283 |
</div>
|
| 1284 |
<div class="d-flex align-items-center">
|
| 1285 |
<button class="btn btn-sm btn-outline-secondary cart-qty-btn" data-id="${id}" data-op="-1">-</button>
|
|
|
|
| 1288 |
</div>
|
| 1289 |
</div>`;
|
| 1290 |
}
|
| 1291 |
+
cartTotalEl.textContent = total.toLocaleString('ru-RU', {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' ₸';
|
| 1292 |
};
|
| 1293 |
|
| 1294 |
const addToCart = (product, variant) => {
|
|
|
|
| 1322 |
const btn = document.createElement('button');
|
| 1323 |
btn.type = 'button';
|
| 1324 |
btn.className = 'list-group-item list-group-item-action';
|
| 1325 |
+
btn.innerHTML = `${variant.option_value} - <strong>${parseFloat(variant.price).toLocaleString('ru-RU', {minimumFractionDigits: 2})} ₸</strong> <span class="badge bg-secondary float-end">Остаток: ${variant.stock}</span>`;
|
| 1326 |
btn.addEventListener('click', () => {
|
| 1327 |
addToCart(product, variant);
|
| 1328 |
variantSelectModal.hide();
|
|
|
|
| 1431 |
const phone = document.getElementById('whatsapp-phone').value.replace(/\\D/g, '');
|
| 1432 |
const receiptUrl = document.getElementById('receipt-url').value;
|
| 1433 |
if (phone && receiptUrl) {
|
| 1434 |
+
const fullPhone = phone;
|
| 1435 |
const message = encodeURIComponent(`Ваш чек: ${receiptUrl}`);
|
| 1436 |
window.open(`https://wa.me/${fullPhone}?text=${message}`, '_blank');
|
| 1437 |
} else {
|
|
|
|
| 1511 |
{% for v in p.variants %}
|
| 1512 |
<tr>
|
| 1513 |
<td>{{ v.option_value }}</td>
|
| 1514 |
+
<td>{{ format_currency_py(v.price) }} ₸</td>
|
| 1515 |
+
<td>{{ format_currency_py(v.cost_price) }} ₸</td>
|
| 1516 |
<td>{{ v.stock }}</td>
|
| 1517 |
</tr>
|
| 1518 |
{% else %}
|
|
|
|
| 1748 |
<td>{{ t.timestamp[:16]|replace('T', ' ') }}</td>
|
| 1749 |
<td><span class="badge bg-{{'primary' if t.type == 'sale' else 'warning'}}">{{'Продажа' if t.type == 'sale' else 'Возврат'}}</span></td>
|
| 1750 |
<td>{{ t.user_name }}</td><td>{{ t.kassa_name }}</td>
|
| 1751 |
+
<td class="fw-bold">{{ format_currency_py(t.total_amount) }} ₸</td>
|
| 1752 |
<td><span class="badge bg-{{'success' if t.status == 'completed' else 'secondary'}}">{{t.status}}</span></td>
|
| 1753 |
<td>
|
| 1754 |
<ul class="list-unstyled mb-0 small">
|
|
|
|
| 1785 |
<div class="card-header"><h5 class="mb-0">Сводный отчет за период</h5></div>
|
| 1786 |
<div class="card-body">
|
| 1787 |
<ul class="list-group list-group-flush">
|
| 1788 |
+
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-chart-bar me-2 text-primary"></i>Выручка (за вычетом возвратов)</span> <strong>{{ format_currency_py(stats.total_revenue) }} ₸</strong></li>
|
| 1789 |
+
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-cogs me-2 text-secondary"></i>Себестоимость проданных товаров</span> <strong>{{ format_currency_py(stats.total_cogs) }} ₸</strong></li>
|
| 1790 |
+
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-piggy-bank me-2 text-info"></i>Валовая прибыль</span> <strong class="text-info">{{ format_currency_py(stats.gross_profit) }} ₸</strong></li>
|
| 1791 |
+
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-receipt me-2 text-warning"></i>Расходы (операционные)</span> <strong class="text-warning">-{{ format_currency_py(stats.total_expenses) }} ₸</strong></li>
|
| 1792 |
+
<li class="list-group-item d-flex justify-content-between"><span><i class="fas fa-users-cog me-2 text-danger"></i>Расходы (зарплаты)</span> <strong class="text-danger">-{{ format_currency_py(stats.total_salary_expenses) }} ₸</strong></li>
|
| 1793 |
+
<li class="list-group-item d-flex justify-content-between fs-5"><span><i class="fas fa-check-circle me-2 text-success"></i>Чистая прибыль</span> <strong class="text-success">{{ format_currency_py(stats.net_profit) }} ₸</strong></li>
|
| 1794 |
</ul>
|
| 1795 |
</div>
|
| 1796 |
</div>
|
|
|
|
| 1801 |
<thead><tr><th>Кассир</th><th class="text-end">К выплате</th></tr></thead>
|
| 1802 |
<tbody>
|
| 1803 |
{% for name, payout in stats.cashier_payouts %}
|
| 1804 |
+
<tr><td>{{ name }}</td><td class="text-end">{{ format_currency_py(payout) }} ₸</td></tr>
|
| 1805 |
{% else %}<tr><td colspan="2" class="text-center text-muted">Нет данных для расчета.</td></tr>
|
| 1806 |
{% endfor %}
|
| 1807 |
</tbody>
|
|
|
|
| 1817 |
<thead><tr><th>Кассир</th><th>Чеков</th><th class="text-end">Сумма</th></tr></thead>
|
| 1818 |
<tbody>
|
| 1819 |
{% for name, data in stats.sales_by_cashier %}
|
| 1820 |
+
<tr><td>{{ name }}</td><td>{{ data.count }}</td><td class="text-end">{{ format_currency_py(data.total) }} ₸</td></tr>
|
| 1821 |
{% else %}<tr><td colspan="3" class="text-center text-muted">Нет продаж за выбранный период.</td></tr>
|
| 1822 |
{% endfor %}
|
| 1823 |
</tbody>
|
|
|
|
| 1834 |
<tr>
|
| 1835 |
<td><small>{{ expense.timestamp[:10] }}</small></td>
|
| 1836 |
<td>{{ expense.description }}</td>
|
| 1837 |
+
<td class="text-end">{{ format_currency_py(expense.amount) }} ₸</td>
|
| 1838 |
</tr>
|
| 1839 |
{% else %}<tr><td colspan="3" class="text-center text-muted">Нет расходов за выбранный период.</td></tr>
|
| 1840 |
{% endfor %}
|
|
|
|
| 1869 |
<tr>
|
| 1870 |
<td><strong>{{ item.name }}</strong><br><small class="text-muted">{{ item.variant_name }}</small></td>
|
| 1871 |
<td class="text-end">{{ item.total_qty_sold }}</td>
|
| 1872 |
+
<td class="text-end">{{ format_currency_py(item.total_revenue) }} ₸</td>
|
| 1873 |
+
<td class="text-end">{{ format_currency_py(item.inventory_value) }} ₸</td>
|
| 1874 |
+
<td class="text-end text-secondary">{{ format_currency_py(item.total_investment) }} ₸</td>
|
| 1875 |
<td class="text-end fw-bold
|
| 1876 |
{% if item.payback > 0 %}payback-positive
|
| 1877 |
{% elif item.payback < 0 %}payback-negative
|
| 1878 |
{% else %}payback-zero{% endif %}">
|
| 1879 |
+
{{ format_currency_py(item.payback) }} ₸
|
| 1880 |
</td>
|
| 1881 |
</tr>
|
| 1882 |
{% else %}
|
|
|
|
| 1905 |
<li class="list-group-item d-flex justify-content-between align-items-center">
|
| 1906 |
<div>{{ u.name }} <br>
|
| 1907 |
<small class="text-muted">
|
| 1908 |
+
{% if u.payment_type == 'salary' %}Зарплата: {{ format_currency_py(u.payment_value) }} ₸/мес.
|
| 1909 |
{% elif u.payment_type == 'percentage' %}Процент: {{ u.payment_value }}%
|
| 1910 |
{% endif %}
|
| 1911 |
</small>
|
|
|
|
| 1935 |
<ul class="list-group">
|
| 1936 |
{% for k in kassas %}
|
| 1937 |
<li class="list-group-item d-flex justify-content-between align-items-center">
|
| 1938 |
+
<div>{{ k.name }} <br><small class="fw-bold">{{ format_currency_py(k.balance) }} ₸</small></div>
|
| 1939 |
<form action="{{ url_for('manage_kassa') }}" method="POST" onsubmit="return confirm('Удалить кассу?');"><input type="hidden" name="action" value="delete"><input type="hidden" name="id" value="{{ k.id }}"><button type="submit" class="btn btn-sm btn-danger"><i class="fas fa-trash"></i></button></form>
|
| 1940 |
</li>
|
| 1941 |
{% endfor %}
|
|
|
|
| 1978 |
<tr>
|
| 1979 |
<td><small>{{ e.timestamp[:16]|replace('T', ' ') }}</small></td>
|
| 1980 |
<td>{{ e.description }}</td>
|
| 1981 |
+
<td class="text-end fw-bold">{{ format_currency_py(e.amount) }} ₸</td>
|
| 1982 |
</tr>
|
| 1983 |
{% else %}
|
| 1984 |
<tr><td colspan="3" class="text-center">Расходов пока нет</td></tr>
|
|
|
|
| 2015 |
<div class="mb-3"><label class="form-label">Имя</label><input type="text" name="name" class="form-control" required></div>
|
| 2016 |
<div class="mb-3"><label class="form-label">ПИН-код</label><input type="password" name="pin" class="form-control" required></div>
|
| 2017 |
<div class="mb-3"><label class="form-label">Тип оплаты</label><select name="payment_type" class="form-select"><option value="percentage">Процент от продаж</option><option value="salary">Фиксированная зарплата</option></select></div>
|
| 2018 |
+
<div class="mb-3"><label class="form-label">Значение</label><input type="text" name="payment_value" class="form-control" inputmode="decimal" value="0" required><small class="form-text text-muted">Для процентов - число (напр. 5), для зарплаты - сумма в тенге.</small></div>
|
| 2019 |
</div>
|
| 2020 |
<div class="modal-footer"><button type="submit" class="btn btn-primary">Сохранить</button></div>
|
| 2021 |
</form>
|
|
|
|
| 2103 |
<td><small class="text-muted">{{ t.id[:8] }}</small></td>
|
| 2104 |
<td>{{ t.timestamp[:16]|replace('T', ' ') }}</td>
|
| 2105 |
<td><span class="badge bg-{{'primary' if t.type == 'sale' else 'warning'}}">{{'Продажа' if t.type == 'sale' else 'Возврат'}}</span></td>
|
| 2106 |
+
<td class="fw-bold">{{ format_currency_py(t.total_amount) }} ₸</td>
|
| 2107 |
<td><span class="badge bg-{{'success' if t.status == 'completed' else 'secondary'}}">{{t.status}}</span></td>
|
| 2108 |
<td>
|
| 2109 |
{% if t.type == 'sale' and t.status == 'completed' %}
|