my-project/templates/product_detail.html
2026-03-23 11:35:48 +05:00

427 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block title %}{{ product.product_name }} - детальная информация{% endblock %}
{% block content %}
<div class="container">
<!-- Навигация -->
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="page-title">{{ product.product_name }}</h2>
<div>
<a href="{{ url_for('edit_product', product_id=product.id) }}" class="btn btn-primary me-2">
<i class="fas fa-edit"></i> Редактировать
</a>
<a href="{{ url_for('catalog') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> Назад в каталог
</a>
</div>
</div>
<div class="row">
<!-- Левая колонка - основная информация -->
<div class="col-md-6">
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, var(--steel-blue) 0%, var(--light-steel-blue) 100%); color: white;">
<h5 class="mb-0"><i class="fas fa-info-circle"></i> Основная информация</h5>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<td style="width: 40%;"><strong>Артикул:</strong></td>
<td>{{ product.article }}</td>
</tr>
<tr>
<td><strong>Категория:</strong></td>
<td>{{ product.category_name }}</td>
</tr>
<tr>
<td><strong>Материал:</strong></td>
<td>
{% if product.material_name %}
{{ product.material_name }}
{% else %}
<span class="text-muted">Не указан</span>
{% endif %}
</td>
</tr>
{% if product.description %}
<tr>
<td><strong>Описание:</strong></td>
<td>{{ product.description }}</td>
</tr>
{% endif %}
<tr>
<td><strong>Базовая цена:</strong></td>
<td><span class="fw-bold fs-4" style="color: var(--steel-blue);">{{ product.min_price|int }} руб.</span></td>
</tr>
<tr>
<td><strong>В наличии:</strong></td>
<td>
{% if product.quantity_in_stock and product.quantity_in_stock > 0 %}
<span class="badge bg-success" style="font-size: 1rem;">{{ product.quantity_in_stock }} шт.</span>
{% else %}
<span class="badge bg-danger">Нет в наличии</span>
{% endif %}
</td>
</tr>
</table>
</div>
</div>
<!-- Информация о материале -->
{% if material %}
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, var(--light-steel-blue) 0%, #9fb6d4 100%);">
<h5 class="mb-0"><i class="fas fa-cube"></i> Информация о материале</h5>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<td style="width: 40%;"><strong>Материал:</strong></td>
<td>{{ material.material_name }}</td>
</tr>
{% if material.color %}
<tr>
<td><strong>Цвет:</strong></td>
<td>{{ material.color }}</td>
</tr>
{% endif %}
{% if material.supplier %}
<tr>
<td><strong>Поставщик:</strong></td>
<td>{{ material.supplier }}</td>
</tr>
{% endif %}
<tr>
<td><strong>Цена за ед.:</strong></td>
<td>{{ material.price_per_unit|int }} руб./{{ material.unit }}</td>
</tr>
<tr>
<td><strong>На складе:</strong></td>
<td>
{% if material.in_stock and material.in_stock > 0 %}
<span class="badge bg-success">{{ material.in_stock }} {{ material.unit }}</span>
{% else %}
<span class="badge bg-danger">Нет</span>
{% endif %}
</td>
</tr>
</table>
</div>
</div>
{% endif %}
</div>
<!-- Правая колонка - производство -->
<div class="col-md-6">
<!-- Производственные характеристики -->
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, var(--light-steel-blue) 0%, #9fb6d4 100%);">
<h5 class="mb-0"><i class="fas fa-cogs"></i> Производственные характеристики</h5>
</div>
<div class="card-body">
<table class="table table-borderless">
<tr>
<td style="width: 50%;"><strong>Время изготовления:</strong></td>
<td>
{% if product.production_time_days %}
{{ product.production_time_days }} дн.
{% else %}
{{ material.production_time_days if material else 1 }} дн. (по умолчанию)
{% endif %}
</td>
</tr>
<tr>
<td><strong>Производительность:</strong></td>
<td>
{% if product.daily_capacity %}
{{ product.daily_capacity }} шт/день
{% else %}
{{ material.daily_capacity if material else 10 }} шт/день (по умолчанию)
{% endif %}
</td>
</tr>
<tr>
<td><strong>Себестоимость:</strong></td>
<td>
{% if product.cost_price %}
{{ product.cost_price|int }} руб.
{% elif material and material.price_per_unit %}
{{ (material.price_per_unit * 1.3)|int }} руб. (расчетная)
{% else %}
<span class="text-muted">Не указана</span>
{% endif %}
</td>
</tr>
<tr>
<td><strong>Прибыль с единицы:</strong></td>
<td>
{% set cost = product.cost_price or (material.price_per_unit * 1.3 if material else 0) %}
{% if cost and cost > 0 %}
<span class="fw-bold text-success">{{ (product.min_price - cost)|int }} руб.</span>
<small class="text-muted">({{ ((product.min_price - cost) / product.min_price * 100)|round(1) }}%)</small>
{% else %}
<span class="text-muted">—</span>
{% endif %}
</td>
</tr>
</table>
</div>
</div>
<!-- Расчет стоимости с фиксированными процентами -->
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, var(--steel-blue) 0%, var(--light-steel-blue) 100%); color: white;">
<h5 class="mb-0"><i class="fas fa-truck"></i> Расчет стоимости со сборкой и доставкой</h5>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-md-6">
<label for="quantity" class="form-label">Количество</label>
<input type="number" class="form-control" id="quantity" value="1" min="1" max="{{ product.quantity_in_stock or 100 }}"
onkeypress="return isNumberKey(event)"
oninput="this.value = this.value.replace(/[^0-9]/g, '')">
</div>
</div>
<!-- Результаты расчета -->
<div class="table-responsive mt-3">
<table class="table table-bordered">
<tr>
<th style="width: 50%;">Базовая стоимость:</th>
<td class="text-end fw-bold" id="baseCost">0 руб.</td>
</tr>
<tr>
<th>Стоимость сборки (15%):</th>
<td class="text-end" id="assemblyCost">0 руб.</td>
</tr>
<tr>
<th>Стоимость доставки (10%):</th>
<td class="text-end" id="deliveryCost">0 руб.</td>
</tr>
<tr class="table-info">
<th><span class="fs-5">ИТОГОВАЯ СТОИМОСТЬ:</span></th>
<td class="text-end"><span class="fw-bold fs-4" style="color: var(--steel-blue);" id="totalCost">0 руб.</span></td>
</tr>
</table>
</div>
</div>
</div>
<!-- Статистика продаж -->
<div class="card mb-4">
<div class="card-header" style="background: linear-gradient(135deg, var(--light-steel-blue) 0%, #9fb6d4 100%);">
<h5 class="mb-0"><i class="fas fa-chart-bar"></i> Статистика продаж</h5>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-4">
<div class="p-3">
<div class="h3 mb-1 fw-bold" style="color: var(--steel-blue);">{{ orders_stats.total_orders or 0 }}</div>
<small class="text-muted">Всего заказов</small>
</div>
</div>
<div class="col-4">
<div class="p-3">
<div class="h3 mb-1 fw-bold" style="color: var(--steel-blue);">{{ orders_stats.total_sold or 0 }}</div>
<small class="text-muted">Продано единиц</small>
</div>
</div>
<div class="col-4">
<div class="p-3">
<div class="h3 mb-1 fw-bold" style="color: var(--steel-blue);">{{ (orders_stats.avg_order_size or 0)|int }}</div>
<small class="text-muted">Средний заказ</small>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- JavaScript для калькулятора -->
<script>
// Функция для проверки ввода только цифр
function isNumberKey(evt) {
var charCode = (evt.which) ? evt.which : evt.keyCode;
// Разрешаем цифры, backspace, delete, tab, enter, стрелки
if (charCode == 8 || charCode == 46 || charCode == 9 || charCode == 13 ||
charCode == 37 || charCode == 38 || charCode == 39 || charCode == 40) {
return true;
}
// Разрешаем только цифры (0-9)
if (charCode >= 48 && charCode <= 57) {
return true;
}
return false;
}
document.addEventListener('DOMContentLoaded', function() {
// Получаем данные товара
const price = {{ product.min_price }};
const assemblyPercent = 15;
const deliveryPercent = 10;
// Элементы для расчета стоимости
const quantityInput = document.getElementById('quantity');
const baseCostSpan = document.getElementById('baseCost');
const assemblyCostSpan = document.getElementById('assemblyCost');
const deliveryCostSpan = document.getElementById('deliveryCost');
const totalCostSpan = document.getElementById('totalCost');
// Функция расчета стоимости
function calculateTotalCost() {
const quantity = parseInt(quantityInput.value) || 1;
// Базовая стоимость
const baseCost = price * quantity;
// Стоимость сборки и доставки (фиксированные проценты)
const assemblyCost = baseCost * assemblyPercent / 100;
const deliveryCost = baseCost * deliveryPercent / 100;
// Итоговая стоимость
const totalCost = baseCost + assemblyCost + deliveryCost;
// Обновляем значения
baseCostSpan.textContent = baseCost.toLocaleString('ru-RU') + ' руб.';
assemblyCostSpan.textContent = assemblyCost.toLocaleString('ru-RU') + ' руб.';
deliveryCostSpan.textContent = deliveryCost.toLocaleString('ru-RU') + ' руб.';
totalCostSpan.textContent = totalCost.toLocaleString('ru-RU') + ' руб.';
}
// Слушатель для изменения количества
quantityInput.addEventListener('input', calculateTotalCost);
// Запрещаем вставку текста
quantityInput.addEventListener('paste', function(e) {
e.preventDefault();
return false;
});
// Первоначальный расчет
calculateTotalCost();
});
</script>
<style>
.page-title {
color: var(--steel-blue);
font-size: 2rem;
font-weight: 600;
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
}
.card {
border: 1px solid var(--light-steel-blue);
border-radius: 12px;
box-shadow: 0 4px 8px rgba(70, 130, 180, 0.1);
transition: all 0.2s ease;
}
.card:hover {
box-shadow: 0 8px 16px rgba(70, 130, 180, 0.15);
}
.table-borderless td {
padding: 8px 0;
border: none;
}
.table-bordered {
border-color: var(--light-steel-blue);
}
.table-bordered th,
.table-bordered td {
border-color: var(--light-steel-blue);
padding: 12px;
}
.table-info {
background-color: rgba(70, 130, 180, 0.1);
}
.form-label {
color: var(--steel-blue);
font-weight: 500;
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
}
.form-control {
border: 2px solid var(--light-steel-blue);
border-radius: 8px;
padding: 10px 15px;
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
}
.form-control:focus {
border-color: var(--steel-blue);
box-shadow: 0 0 0 0.2rem rgba(70, 130, 180, 0.25);
outline: none;
}
.btn {
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
border: none;
border-radius: 8px;
padding: 10px 20px;
transition: all 0.2s ease;
}
.btn-primary {
background: linear-gradient(135deg, var(--steel-blue) 0%, #3a6d99 100%);
color: white;
}
.btn-primary:hover {
background: linear-gradient(135deg, #3a6d99 0%, var(--steel-blue) 100%);
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(70, 130, 180, 0.3);
}
.btn-secondary {
background: linear-gradient(135deg, var(--light-steel-blue) 0%, #9fb6d4 100%);
color: #2c3e50;
}
.btn-secondary:hover {
background: linear-gradient(135deg, #9fb6d4 0%, var(--light-steel-blue) 100%);
transform: translateY(-2px);
box-shadow: 0 4px 10px rgba(176, 196, 222, 0.3);
}
.badge {
font-family: 'Franklin Gothic', 'Arial Narrow', Arial, sans-serif;
padding: 6px 12px;
}
.bg-light {
background-color: #f8f9fa !important;
}
.text-primary {
color: var(--steel-blue) !important;
}
.text-success {
color: #28a745 !important;
}
@media (max-width: 768px) {
.page-title {
font-size: 1.5rem;
}
.d-flex.justify-content-between {
flex-direction: column;
gap: 10px;
}
.d-flex.justify-content-between .btn {
width: 100%;
}
}
</style>
{% endblock %}