from flask import Flask, render_template_string, request, redirect, url_for, flash, send_file, jsonify import json import os import logging import threading import time from datetime import datetime from huggingface_hub import HfApi, hf_hub_download from huggingface_hub.utils import RepositoryNotFoundError from werkzeug.utils import secure_filename import uuid import html import random app = Flask(__name__) app.secret_key = os.getenv("FLASK_SECRET_KEY", "zzirix_secret_key_for_cart") DATA_FILE = 'data_zzirix.json' REPO_ID = "Kgshop/clients" HF_TOKEN_WRITE = os.getenv("HF_TOKEN") HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") or HF_TOKEN_WRITE LOGO_URL = "https://huggingface.co/spaces/Kgshop/Zzirixadm/resolve/main/Picsart_25-03-20_15-38-36-600.jpg" logging.basicConfig(level=logging.INFO) def initialize_data_structure(data): if not isinstance(data, dict): data = {'categories': [], 'products': [], 'orders': {}} data.setdefault('categories', []) data.setdefault('products', []) data.setdefault('orders', {}) if any(p.get('category') == 'Без категории' for p in data['products']) and 'Без категории' not in data['categories']: data['categories'].append('Без категории') for product in data['products']: if 'id' not in product: product['id'] = str(uuid.uuid4()) product.setdefault('name', 'Без названия') product.setdefault('description', '') product.setdefault('category', 'Без категории') product.setdefault('price', 0.0) product.setdefault('colors', []) product.setdefault('models', []) product.setdefault('photos', []) product.setdefault('in_stock', True) product.setdefault('is_top', False) if not product['photos'] and 'media' in product and product['media']: product['photos'] = [m['filename'] for m in product['media'] if m['type'] == 'photo'] if 'media' in product: del product['media'] data['categories'] = sorted(list(set(data['categories'])), key=lambda x: (x != 'Без категории', x)) return data def load_data(): try: if not os.path.exists(DATA_FILE) or os.path.getsize(DATA_FILE) == 0: download_db_from_hf() if os.path.exists(DATA_FILE) and os.path.getsize(DATA_FILE) > 0: with open(DATA_FILE, 'r', encoding='utf-8') as file: data = json.load(file) else: data = {'products': [], 'categories': [], 'orders': {}} return initialize_data_structure(data) except Exception as e: return initialize_data_structure({}) def save_data(data): try: temp_file = DATA_FILE + '.tmp' with open(temp_file, 'w', encoding='utf-8') as file: json.dump(data, file, ensure_ascii=False, indent=4) os.replace(temp_file, DATA_FILE) upload_db_to_hf() except Exception as e: if os.path.exists(temp_file): os.remove(temp_file) raise def upload_db_to_hf(): if not HF_TOKEN_WRITE: return try: api = HfApi() api.upload_file( path_or_fileobj=DATA_FILE, path_in_repo=DATA_FILE, repo_id=REPO_ID, repo_type="dataset", token=HF_TOKEN_WRITE, commit_message=f"Backup {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ) except Exception as e: pass def download_db_from_hf(): if not HF_TOKEN_READ: if not os.path.exists(DATA_FILE): with open(DATA_FILE, 'w', encoding='utf-8') as f: json.dump(initialize_data_structure({}), f) return try: hf_hub_download( repo_id=REPO_ID, filename=DATA_FILE, repo_type="dataset", token=HF_TOKEN_READ, local_dir=".", local_dir_use_symlinks=False, force_download=True ) except Exception as e: if not os.path.exists(DATA_FILE): with open(DATA_FILE, 'w', encoding='utf-8') as f: json.dump(initialize_data_structure({}), f) def periodic_backup(): while True: time.sleep(3600) try: data = load_data() save_data(data) except Exception as e: pass def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in {'png', 'jpg', 'jpeg', 'gif', 'webp', 'mp4', 'mov', 'webm'} BASE_STYLE = ''' :root { --primary-color: #3B82F6; --primary-dark-color: #2563eb; --accent-color: #10b981; --accent-dark-color: #059669; --danger-color: #ef4444; --danger-dark-color: #dc2626; --background-light: linear-gradient(135deg, #f0f2f5, #e0e5ec); --background-dark: linear-gradient(135deg, #1f2937, #374151); --card-background-light: #ffffff; --card-background-dark: #2d3748; --text-color-light: #2d3748; --text-color-dark: #e2e8f0; --secondary-text-color-light: #718096; --secondary-text-color-dark: #a0aec0; --border-color-light: #e2e8f0; --border-color-dark: #4a5568; --shadow-light: 0 8px 25px rgba(0, 0, 0, 0.1); --shadow-hover-light: 0 12px 35px rgba(0, 0, 0, 0.18); --shadow-dark: 0 8px 25px rgba(0, 0, 0, 0.35); --shadow-hover-dark: 0 12px 35px rgba(0, 0, 0, 0.5); --border-radius-large: 22px; --border-radius-medium: 12px; --border-radius-small: 8px; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Roboto', sans-serif; background: var(--background-light); color: var(--text-color-light); line-height: 1.6; transition: background 0.3s, color 0.3s; min-height: 100vh; display: flex; flex-direction: column; } body.dark-mode { background: var(--background-dark); color: var(--text-color-dark); } .container { max-width: 1400px; margin: 0 auto; padding: 30px; flex-grow: 1; } .header { display: flex; justify-content: space-between; align-items: center; padding: 20px 30px; border-bottom: none; margin-bottom: 30px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); border-radius: var(--border-radius-large); background: var(--card-background-light); } body.dark-mode .header { border-bottom-color: var(--border-color-dark); box-shadow: 0 2px 10px rgba(0,0,0,0.2); background: var(--card-background-dark); } .header-info { display: flex; align-items: center; } .header-logo { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); transition: transform 0.3s ease, box-shadow 0.3s ease; margin-right: 15px; } .header-logo:hover { transform: scale(1.08); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25); } .header h1 { font-size: 2rem; font-weight: 800; color: var(--primary-color); } .header-actions { display: flex; gap: 10px; align-items: center; } .theme-toggle { background: none; border: none; font-size: 1.8rem; cursor: pointer; color: var(--secondary-text-color-light); transition: color 0.3s ease, transform 0.2s ease; padding: 5px; } body.dark-mode .theme-toggle { color: var(--secondary-text-color-dark); } .theme-toggle:hover { color: var(--accent-color); transform: rotate(30deg); } .flash { padding: 18px; margin-bottom: 25px; border-radius: var(--border-radius-medium); font-weight: 500; border: 1px solid transparent; font-size: 1.1rem; } .flash.success { background-color: var(--accent-color); color: white; border-color: var(--accent-dark-color); } .flash.error { background-color: var(--danger-color); color: white; border-color: var(--danger-dark-color); } .filters-container { margin: 30px 0; display: flex; flex-wrap: wrap; gap: 15px; justify-content: center; } .search-container { margin: 25px 0; text-align: center; } #search-input { width: 90%; max-width: 600px; padding: 14px 22px; font-size: 1rem; border: none; border-radius: var(--border-radius-medium); outline: none; box-shadow: var(--shadow-light); transition: all 0.3s ease; background-color: var(--card-background-light); color: var(--text-color-light); } body.dark-mode #search-input { border-color: var(--border-color-dark); background-color: var(--card-background-dark); color: var(--text-color-dark); box-shadow: var(--shadow-dark); } #search-input:focus { border-color: var(--primary-color); box-shadow: 0 0 10px rgba(59, 130, 246, 0.4); } .category-filter { padding: 12px 25px; border: none; border-radius: var(--border-radius-medium); background-color: var(--card-background-light); cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); font-size: 1rem; font-weight: 500; color: var(--text-color-light); box-shadow: var(--shadow-light); } body.dark-mode .category-filter { border-color: var(--border-color-dark); background-color: var(--card-background-dark); color: var(--text-color-dark); box-shadow: var(--shadow-dark); } .category-filter.active, .category-filter:hover { background-color: var(--primary-color); color: white; border-color: var(--primary-color); box-shadow: var(--shadow-hover-light); } body.dark-mode .category-filter.active, body.dark-mode .category-filter:hover { background-color: var(--primary-color); border-color: var(--primary-color); box-shadow: var(--shadow-hover-dark); } .products-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 25px; padding: 15px; } .product { background: var(--card-background-light); border-radius: var(--border-radius-large); padding: 20px; box-shadow: var(--shadow-light); transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease; overflow: hidden; display: flex; flex-direction: column; justify-content: space-between; } body.dark-mode .product { background: var(--card-background-dark); box-shadow: var(--shadow-dark); } .product:hover { transform: translateY(-10px) scale(1.03); box-shadow: var(--shadow-hover-light); } body.dark-mode .product:hover { box-shadow: var(--shadow-hover-dark); } .product-image { width: 100%; aspect-ratio: 1; background-color: #f7f7f7; border-radius: var(--border-radius-medium); overflow: hidden; display: flex; justify-content: center; align-items: center; margin-bottom: 15px; cursor: pointer; } body.dark-mode .product-image { background-color: #343e4d; } .product-image img, .product-image video { max-width: 100%; max-height: 100%; object-fit: contain; transition: transform 0.3s ease; border-radius: var(--border-radius-medium); } .product-image img:hover, .product-image video:hover { transform: scale(1.05); } .product h2 { font-size: 1.2rem; font-weight: 700; margin: 8px 0; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-color-light); } body.dark-mode .product h2 { color: var(--text-color-dark); } .product-price { font-size: 1.3rem; color: var(--danger-color); font-weight: 700; text-align: center; margin: 8px 0 12px; } .product-description { font-size: 0.9rem; color: var(--secondary-text-color-light); text-align: center; margin-bottom: 20px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } body.dark-mode .product-description { color: var(--secondary-text-color-dark); } .product-actions { display: flex; flex-direction: column; gap: 10px; margin-top: auto; } .product-button { display: block; width: 100%; padding: 12px; border: none; border-radius: var(--border-radius-medium); background-color: var(--primary-color); color: white; font-size: 1rem; font-weight: 500; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); text-align: center; text-decoration: none; box-shadow: 0 3px 10px rgba(0,0,0,0.1); } .product-button:hover { background-color: var(--primary-dark-color); box-shadow: 0 6px 15px rgba(59, 130, 246, 0.4); transform: translateY(-2px); } .add-to-cart { background-color: var(--accent-color); box-shadow: 0 3px 10px rgba(16, 185, 129, 0.2); } .add-to-cart:hover { background-color: var(--accent-dark-color); box-shadow: 0 6px 15px rgba(16, 185, 129, 0.4); } #cart-button { position: fixed; bottom: 30px; right: 30px; background-color: var(--danger-color); color: white; border: none; border-radius: 50%; width: 60px; height: 60px; font-size: 1.5rem; cursor: pointer; box-shadow: 0 8px 25px rgba(239, 68, 68, 0.3); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); z-index: 1000; display: flex; align-items: center; justify-content: center; } #cart-button:hover { background-color: var(--danger-dark-color); transform: translateY(-5px) scale(1.08); box-shadow: 0 12px 35px rgba(239, 68, 68, 0.5); } .cart-count { position: absolute; top: -5px; right: -5px; background-color: var(--accent-color); color: white; border-radius: 50%; padding: 4px 8px; font-size: 0.8rem; min-width: 22px; text-align: center; font-weight: 600; } .modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); backdrop-filter: blur(8px); overflow-y: auto; padding: 20px; } .modal-content { background: var(--card-background-light); margin: 50px auto; padding: 40px; border-radius: var(--border-radius-large); width: 95%; max-width: 750px; box-shadow: var(--shadow-hover-light); animation: fadeInScale 0.3s ease-out; max-height: 90vh; overflow-y: auto; -webkit-overflow-scrolling: touch; position: relative; } body.dark-mode .modal-content { background: var(--card-background-dark); color: var(--text-color-dark); box-shadow: var(--shadow-hover-dark); } @keyframes fadeInScale { from { opacity: 0; transform: translateY(-30px) scale(0.95); } to { opacity: 1; transform: translateY(0) scale(1); } } .close { position: absolute; top: 20px; right: 25px; font-size: 2rem; color: var(--secondary-text-color-light); cursor: pointer; transition: color 0.3s, transform 0.2s; } .close:hover { color: var(--danger-color); transform: rotate(90deg); } body.dark-mode .close { color: var(--secondary-text-color-dark); } body.dark-mode .close:hover { color: var(--danger-color); } .modal h2 { font-size: 1.8rem; font-weight: 700; margin-bottom: 25px; text-align: center; } .cart-item { display: flex; align-items: center; padding: 18px 0; border-bottom: 1px solid var(--border-color-light); } body.dark-mode .cart-item { border-bottom-color: var(--border-color-dark); } .cart-item:last-child { border-bottom: none; } .cart-item img { width: 70px; height: 70px; object-fit: contain; border-radius: var(--border-radius-small); margin-right: 20px; background-color: #f7f7f7; } body.dark-mode .cart-item img { background-color: #343e4d; } .cart-item-details { flex-grow: 1; } .cart-item-details strong { font-size: 1.2rem; font-weight: 600; } .cart-item-details p { font-size: 0.95rem; color: var(--secondary-text-color-light); } body.dark-mode .cart-item-details p { color: var(--secondary-text-color-dark); } .cart-item-total { font-size: 1.1rem; font-weight: 700; color: var(--danger-color); } .quantity-input, .color-select, .model-select { width: 100%; padding: 12px; border: 1px solid var(--border-color-light); border-radius: var(--border-radius-medium); font-size: 1rem; margin: 10px 0; background-color: var(--card-background-light); color: var(--text-color-light); box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); } body.dark-mode .quantity-input, body.dark-mode .color-select, body.dark-mode .model-select { border-color: var(--border-color-dark); background-color: var(--card-background-dark); color: var(--text-color-dark); box-shadow: inset 0 1px 3px rgba(0,0,0,0.2); } .qty-controls { display: flex; align-items: center; gap: 10px; margin: 10px 0; } .qty-btn { background-color: var(--primary-color); color: white; border: none; border-radius: var(--border-radius-small); width: 40px; height: 40px; font-size: 1.5rem; cursor: pointer; display: flex; align-items: center; justify-content: center; } .qty-btn:hover { background-color: var(--primary-dark-color); } .qty-controls input { margin: 0; text-align: center; width: calc(100% - 100px); } .modal-buttons { margin-top: 30px; display: flex; justify-content: flex-end; gap: 15px; } .modal-buttons .product-button { width: auto; padding: 12px 25px; } .clear-cart { background-color: var(--danger-color); } .clear-cart:hover { background-color: var(--danger-dark-color); box-shadow: 0 6px 15px rgba(239, 68, 68, 0.4); } .order-button { background-color: var(--accent-color); } .order-button:hover { background-color: var(--accent-dark-color); box-shadow: 0 6px 15px rgba(16, 185, 129, 0.4); } .swiper-container { max-width: 500px; margin: 0 auto 30px; border-radius: var(--border-radius-large); overflow: hidden; box-shadow: var(--shadow-light); } body.dark-mode .swiper-container { box-shadow: var(--shadow-dark); } .swiper-slide { background-color: #f7f7f7; display: flex; justify-content: center; align-items: center; min-height: 300px; } body.dark-mode .swiper-slide { background-color: #343e4d; } .swiper-slide img, .swiper-slide video { max-width: 100%; max-height: 350px; object-fit: contain; } .swiper-button-next, .swiper-button-prev { color: var(--primary-color) !important; background-color: rgba(255,255,255,0.9); border-radius: 50%; width: 45px; height: 45px; display: flex; align-items: center; justify-content: center; transition: background-color 0.3s; font-size: 1.8rem; } .swiper-button-next:hover, .swiper-button-prev:hover { background-color: #fff; } body.dark-mode .swiper-button-next, body.dark-mode .swiper-button-prev { background-color: rgba(45, 55, 72, 0.9); } body.dark-mode .swiper-button-next:hover, body.dark-mode .swiper-button-prev:hover { background-color: #2d3748; } .swiper-pagination-bullet { background-color: var(--primary-color) !important; } .product-detail-modal-inner { background: transparent; padding: 0; margin: 0; box-shadow: none; position: static; } body.dark-mode .product-detail-modal-inner { background: transparent; box-shadow: none; } .product-detail-modal-inner h2 { font-size: 2.2rem; font-weight: 700; margin-bottom: 25px; text-align: center; color: var(--text-color-light); } body.dark-mode .product-detail-modal-inner h2 { color: var(--text-color-dark); } .product-detail-modal-inner p { margin-bottom: 10px; font-size: 1rem; } .product-detail-modal-inner p strong { color: var(--text-color-light); } body.dark-mode .product-detail-modal-inner p strong { color: var(--text-color-dark); } .product-detail-modal-inner .price { font-size: 1.8rem; color: var(--danger-color); font-weight: 700; margin-bottom: 20px; text-align: center; } .product-detail-modal-inner .description { margin-bottom: 20px; white-space: pre-wrap; } .product-detail-modal-actions { display: flex; justify-content: center; gap: 15px; margin-top: 30px; } .product-detail-modal-actions .product-button { width: auto; padding: 12px 25px; } .admin-table { width: 100%; border-collapse: collapse; margin-top: 20px; } .admin-table th, .admin-table td { padding: 12px; border: 1px solid var(--border-color-light); text-align: left; vertical-align: middle; } body.dark-mode .admin-table th, body.dark-mode .admin-table td { border-color: var(--border-color-dark); } .admin-table th { background-color: var(--card-background-light); font-weight: 600; } body.dark-mode .admin-table th { background-color: var(--card-background-dark); } .admin-table-responsive { overflow-x: auto; margin-bottom: 20px; } @media (max-width: 768px) { .container { padding: 15px; } .header { flex-direction: column; align-items: flex-start; gap: 15px; } .header-actions { width: 100%; justify-content: space-between; flex-wrap: wrap; } .header h1 { font-size: 1.6rem; } .category-filter { font-size: 0.85rem; padding: 8px 15px; } .products-grid { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 18px; } .product h2 { font-size: 1.05rem; } .product-price { font-size: 1.15rem; } .product-description { font-size: 0.8rem; } .product-button { font-size: 0.9rem; padding: 10px; } #cart-button { width: 50px; height: 50px; font-size: 1.2rem; bottom: 20px; right: 20px; } .modal-content { margin: 20px auto; padding: 25px; max-height: 95vh; } .close { font-size: 1.6rem; top: 15px; right: 18px; } .modal h2 { font-size: 1.6rem; } .cart-item img { width: 55px; height: 55px; } .product-detail-modal-inner h2 { font-size: 2rem; } .product-detail-modal-inner .price { font-size: 1.6rem; } .product-detail-modal-actions { flex-direction: column; gap: 10px; } } ''' JS_FUNCTIONS = ''' const products = {{ products|tojson }}; let selectedProductId = null; let detailSwiperInstance = null; function toggleTheme() { document.body.classList.toggle('dark-mode'); const icon = document.querySelector('.theme-toggle i'); icon.classList.toggle('fa-moon'); icon.classList.toggle('fa-sun'); localStorage.setItem('theme', document.body.classList.contains('dark-mode') ? 'dark' : 'light'); } if (localStorage.getItem('theme') === 'dark') { document.body.classList.add('dark-mode'); const icon = document.querySelector('.theme-toggle i'); if (icon) icon.classList.replace('fa-moon', 'fa-sun'); } else { const icon = document.querySelector('.theme-toggle i'); if (icon) icon.classList.replace('fa-sun', 'fa-moon'); } function closeModal(modalId) { document.getElementById(modalId).style.display = "none"; if (modalId === 'productDetailModal' && detailSwiperInstance) { detailSwiperInstance.destroy(true, true); detailSwiperInstance = null; } } function openQuantityModal(productId) { selectedProductId = productId; const product = products.find(p => p.id === productId); if (!product) { alert('Ошибка: Товар не найден.'); return; } const colorSelect = document.getElementById('colorSelect'); colorSelect.innerHTML = ''; if (product.colors && product.colors.length > 0) { product.colors.forEach(color => { const option = document.createElement('option'); option.value = color; option.text = color; colorSelect.appendChild(option); }); } else { const option = document.createElement('option'); option.value = 'Не указан'; option.text = 'Цвет не указан'; colorSelect.appendChild(option); } const modelSelect = document.getElementById('modelSelect'); modelSelect.innerHTML = ''; if (product.models && product.models.length > 0) { product.models.forEach(model => { const option = document.createElement('option'); option.value = model; option.text = model; modelSelect.appendChild(option); }); } else { const option = document.createElement('option'); option.value = 'Не указана'; option.text = 'Модель не указана'; modelSelect.appendChild(option); } document.getElementById('quantityModal').style.display = 'block'; document.getElementById('quantityInput').value = 1; } function changeQty(delta) { let input = document.getElementById('quantityInput'); let current = parseInt(input.value) || 1; if(current + delta >= 1) { input.value = current + delta; } } function confirmAddToCart() { if (selectedProductId === null) return; const quantityInput = document.getElementById('quantityInput'); const quantity = parseInt(quantityInput.value) || 1; const color = document.getElementById('colorSelect').value; const model = document.getElementById('modelSelect').value; if (quantity <= 0) { alert("Укажите количество больше 0."); quantityInput.focus(); return; } let cart = JSON.parse(localStorage.getItem('cart') || '[]'); const product = products.find(p => p.id === selectedProductId); const cartItemId = `${product.id}-${color}-${model}`; const existingItem = cart.find(item => item.cartId === cartItemId); if (existingItem) { existingItem.quantity += quantity; } else { cart.push({ cartId: cartItemId, productId: product.id, name: product.name, price: product.price, photo: product.photos && product.photos.length > 0 ? product.photos[0] : '', quantity: quantity, color: color, model: model, discount_percent: 0.0 }); } localStorage.setItem('cart', JSON.stringify(cart)); closeModal('quantityModal'); updateCartButton(); alert(`"${product.name}" добавлен в корзину.`); } function updateCartButton() { const cart = JSON.parse(localStorage.getItem('cart') || '[]'); const cartCount = cart.reduce((sum, item) => sum + item.quantity, 0); document.getElementById('cart-count').textContent = cartCount; document.getElementById('cart-button').style.display = cartCount > 0 ? 'flex' : 'none'; } function openCartModal() { const cart = JSON.parse(localStorage.getItem('cart') || '[]'); const cartContent = document.getElementById('cartContent'); let total = 0; cartContent.innerHTML = cart.length === 0 ? '
Корзина пуста
' : cart.map((item, index) => { const itemTotal = item.price * item.quantity; total += itemTotal; const photoSrc = item.photo ? `https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}` : 'https://via.placeholder.com/60x60?text=No+Image'; return `${item.price.toFixed(2)} с × ${item.quantity} (Цвет: ${item.color}, Модель: ${item.model})
{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}
Товаров пока нет. Загляните позже!
{% endfor %}{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}
В этой категории пока нет товаров.
{% endfor %}| № | Код | Наименование | Кол-во | Ед. | Цена | Скидка % | Сумма |
|---|---|---|---|---|---|---|---|
| {{ loop.index }} | {{ item.productId[:6] }} | {{ item.name }} (Цвет: {{ item.color }}, Мод: {{ item.model }}) | {{ item.quantity }} | шт | {{ "%.2f"|format(item.price) }} | {{ "%.2f"|format(item.get('discount_percent', 0.0)) }} | {% set item_final = item.price * (1 - item.get('discount_percent', 0.0) / 100) * item.quantity %}{{ "%.2f"|format(item_final) }} |
| Сумма без скидки | {{ "%.2f"|format(original) }} |
| Скидка{% if order.get('order_discount_percent', 0) > 0 %} ({{ "%.2f"|format(order.order_discount_percent) }}% на весь чек){% endif %} | {{ "%.2f"|format(total_disc) }} |
| Итого | {{ "%.2f"|format(final) }} |
| Наличными | {{ "%.2f"|format(final) }} |
ID: {{ product.id }}
Категория: {{ product.get('category', 'Без категории') }}
Цена: {{ "%.2f"|format(product['price']) }} с
Описание: {{ product['description'] }}
Цвета: {{ product.get('colors', ['Нет цветов'])|join(', ') }}
Модели: {{ product.get('models', ['Нет моделей'])|join(', ') }}
{% if product.get('photos') and product['photos']|length > 0 %}| ID Заказа | Дата | Клиент | Телефон | Сумма (Итого) | Действия |
|---|---|---|---|---|---|
| {{ order.id }} | {{ order.date }} | {{ order.name }} | {{ order.phone }} | {{ "%.2f"|format(order.get('final_total', order.total)) }} с | Посмотреть Сделать скидку |
| Накладных пока нет. | |||||