fix: Улучшения системы ценообразования комплектов
Исправлены 4 проблемы: 1. Расчёт цены первого товара - улучшена валидация в getProductPrice и calculateFinalPrice 2. Отображение actual_price в Select2 вместо обычной цены 3. Количество по умолчанию = 1 для новых форм компонентов 4. Auto-select текста при клике на поле количества для удобства редактирования Изменённые файлы: - products/forms.py: добавлен __init__ в KitItemForm для quantity.initial = 1 - products/templates/includes/select2-product-init.html: обновлена formatSelectResult - products/templates/productkit_create.html: добавлен focus handler для auto-select - products/templates/productkit_edit.html: добавлен focus handler для auto-select 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Распределение продаж{% endblock %}
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Распределение продаж по партиям (FIFO)</h4></div><div class="card-body">{% if allocations %}<table class="table table-hover table-sm"><thead><tr><th>Продажа</th><th>Товар</th><th>Партия</th><th>Кол-во</th><th>Цена</th><th>Дата</th></tr></thead><tbody>{% for a in allocations %}<tr><td>#{{ a.sale.id }}</td><td>{{ a.sale.product.name }}</td><td>#{{ a.batch.id }}</td><td>{{ a.quantity }}</td><td>{{ a.cost_price }}</td><td>{{ a.sale.date|date:"d.m.Y" }}</td></tr>{% endfor %}</tbody></table>{% else %}<div class="alert alert-info">Распределений не найдено.</div>{% endif %}</div></div>
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Распределение продаж по партиям (FIFO)</h4></div><div class="card-body">{% if allocations %}<table class="table table-hover table-sm"><thead><tr><th>Продажа</th><th>Товар</th><th>Партия</th><th>Кол-во</th><th>Цена</th><th>Дата</th></tr></thead><tbody>{% for a in allocations %}<tr><td>#{{ a.sale.id }}</td><td>{{ a.sale.product.name }}</td><td>#{{ a.batch.id }}</td><td>{{ a.quantity|smart_quantity }}</td><td>{{ a.cost_price }}</td><td>{{ a.sale.date|date:"d.m.Y" }}</td></tr>{% endfor %}</tbody></table>{% else %}<div class="alert alert-info">Распределений не найдено.</div>{% endif %}</div></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Партия товара{% endblock %}
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Партия #{{ batch.id }}: {{ batch.product.name }}</h4></div><div class="card-body"><table class="table table-borderless"><tr><th>Товар:</th><td>{{ batch.product.name }}</td></tr><tr><th>Склад:</th><td>{{ batch.warehouse.name }}</td></tr><tr><th>Количество:</th><td><strong>{{ batch.quantity }} шт</strong></td></tr><tr><th>Цена закупки:</th><td>{{ batch.cost_price }} ₽</td></tr><tr><th>Создана:</th><td>{{ batch.created_at|date:"d.m.Y H:i" }}</td></tr><tr><th>Статус:</th><td>{% if batch.is_active %}<span class="badge bg-success">Активна</span>{% else %}<span class="badge bg-secondary">Неактивна</span>{% endif %}</td></tr></table><h5 class="mt-4">История операций</h5><div class="alert alert-info">История продаж и списаний этой партии.</div><a href="{% url 'inventory:batch-list' %}" class="btn btn-secondary"><i class="bi bi-arrow-left"></i> Вернуться</a></div></div>
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Партия #{{ batch.id }}: {{ batch.product.name }}</h4></div><div class="card-body"><table class="table table-borderless"><tr><th>Товар:</th><td>{{ batch.product.name }}</td></tr><tr><th>Склад:</th><td>{{ batch.warehouse.name }}</td></tr><tr><th>Количество:</th><td><strong>{{ batch.quantity|smart_quantity }} шт</strong></td></tr><tr><th>Цена закупки:</th><td>{{ batch.cost_price }} руб.</td></tr><tr><th>Создана:</th><td>{{ batch.created_at|date:"d.m.Y H:i" }}</td></tr><tr><th>Статус:</th><td>{% if batch.is_active %}<span class="badge bg-success">Активна</span>{% else %}<span class="badge bg-secondary">Неактивна</span>{% endif %}</td></tr></table><h5 class="mt-4">История операций</h5><div class="alert alert-info">История продаж и списаний этой партии.</div><a href="{% url 'inventory:batch-list' %}" class="btn btn-secondary"><i class="bi bi-arrow-left"></i> Вернуться</a></div></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Партии товаров{% endblock %}
|
||||
{% block inventory_content %}
|
||||
<div class="card">
|
||||
@@ -28,8 +29,8 @@
|
||||
</td>
|
||||
<td>{{ batch.product.name }}</td>
|
||||
<td>{{ batch.warehouse.name }}</td>
|
||||
<td>{{ batch.quantity }}</td>
|
||||
<td>{{ batch.cost_price }} ₽</td>
|
||||
<td>{{ batch.quantity|smart_quantity }}</td>
|
||||
<td>{{ batch.cost_price }} руб.</td>
|
||||
<td>{{ batch.created_at|date:"d.m.Y" }}</td>
|
||||
<td>
|
||||
<a href="{% url 'inventory:batch-detail' batch.pk %}" class="btn btn-sm btn-outline-info" title="Просмотр партии">
|
||||
|
||||
@@ -3,147 +3,310 @@
|
||||
{% block title %}Склад{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-5">
|
||||
<div class="row mb-4">
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row mb-5">
|
||||
<div class="col-12">
|
||||
<h1 class="display-5">Управление складом</h1>
|
||||
<p class="lead text-muted">Здесь будут инструменты для управления инвентаризацией и складским учетом</p>
|
||||
<h1 class="mb-2">Управление складом</h1>
|
||||
<p class="text-muted">Выберите операцию для работы</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Основные операции -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-building"></i> Управление складами
|
||||
</h5>
|
||||
<p class="card-text text-muted">Создание и управление физическими складами</p>
|
||||
<a href="{% url 'inventory:warehouse-list' %}" class="btn btn-outline-primary">Перейти</a>
|
||||
<!-- Основные операции -->
|
||||
<div class="row mb-5">
|
||||
<div class="col-12">
|
||||
<h5 class="text-uppercase text-muted mb-3">
|
||||
<small>Основные операции</small>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-5">
|
||||
<!-- Управление складами -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:warehouse-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card primary-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper primary-icon mb-3">
|
||||
<i class="bi bi-building"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Управление складами</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-arrow-down-square"></i> Приход товара
|
||||
</h5>
|
||||
<p class="card-text text-muted">Регистрация поступления товаров на склад</p>
|
||||
<a href="{% url 'inventory:incoming-list' %}" class="btn btn-outline-primary">Перейти</a>
|
||||
<!-- Приход товара -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:incoming-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card success-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper success-icon mb-3">
|
||||
<i class="bi bi-arrow-down-square"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Приход товара</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-arrow-up-square"></i> Реализация товара
|
||||
</h5>
|
||||
<p class="card-text text-muted">Учет проданных товаров с применением FIFO</p>
|
||||
<a href="{% url 'inventory:sale-list' %}" class="btn btn-outline-primary">Перейти</a>
|
||||
<!-- Реализация товара -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:sale-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card warning-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper warning-icon mb-3">
|
||||
<i class="bi bi-arrow-up-square"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Реализация товара</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-clipboard-check"></i> Инвентаризация
|
||||
</h5>
|
||||
<p class="card-text text-muted">Проверка фактических остатков и корректировка</p>
|
||||
<a href="{% url 'inventory:inventory-list' %}" class="btn btn-outline-primary">Перейти</a>
|
||||
<!-- Инвентаризация -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:inventory-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card info-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper info-icon mb-3">
|
||||
<i class="bi bi-clipboard-check"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Инвентаризация</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Дополнительные операции -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-x-circle"></i> Списание товара
|
||||
</h5>
|
||||
<p class="card-text text-muted">Списание брака, порчи, недостач</p>
|
||||
<a href="{% url 'inventory:writeoff-list' %}" class="btn btn-outline-secondary">Перейти</a>
|
||||
<!-- Списание товара -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:writeoff-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card danger-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper danger-icon mb-3">
|
||||
<i class="bi bi-x-circle"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Списание товара</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-arrow-left-right"></i> Перемещение товара
|
||||
</h5>
|
||||
<p class="card-text text-muted">Перемещение между складами с сохранением партийности</p>
|
||||
<a href="{% url 'inventory:transfer-list' %}" class="btn btn-outline-secondary">Перейти</a>
|
||||
<!-- Перемещение товара -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:transfer-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card secondary-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper secondary-icon mb-3">
|
||||
<i class="bi bi-arrow-left-right"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Перемещение товара</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Справочная информация -->
|
||||
<div class="row mb-5">
|
||||
<div class="col-12">
|
||||
<h5 class="text-uppercase text-muted mb-3">
|
||||
<small>Справочная информация</small>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<!-- Остатки товаров -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:stock-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card stock-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper stock-icon mb-3">
|
||||
<i class="bi bi-box-seam"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Остатки товаров</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Справочная информация -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-box-seam"></i> Остатки товаров
|
||||
</h5>
|
||||
<p class="card-text text-muted">Просмотр текущих остатков по складам и товарам</p>
|
||||
<a href="{% url 'inventory:stock-list' %}" class="btn btn-outline-info">Перейти</a>
|
||||
<!-- Партии товаров -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:batch-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card batch-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper batch-icon mb-3">
|
||||
<i class="bi bi-diagram-3"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Партии товаров</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-diagram-3"></i> Партии товаров
|
||||
</h5>
|
||||
<p class="card-text text-muted">История партий и их распределение</p>
|
||||
<a href="{% url 'inventory:batch-list' %}" class="btn btn-outline-info">Перейти</a>
|
||||
<!-- Журнал операций -->
|
||||
<div class="col-lg-4 col-md-6">
|
||||
<a href="{% url 'inventory:movement-list' %}" class="card-link">
|
||||
<div class="card h-100 compact-card journal-card">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<div class="icon-wrapper journal-icon mb-3">
|
||||
<i class="bi bi-journal-check"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Журнал операций</h6>
|
||||
<div class="mt-auto">
|
||||
<span class="btn-text">Перейти →</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<i class="bi bi-journal-check"></i> Журнал операций
|
||||
</h5>
|
||||
<p class="card-text text-muted">Полный журнал всех складских движений</p>
|
||||
<a href="{% url 'inventory:movement-list' %}" class="btn btn-outline-info">Перейти</a>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
.card-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.compact-card {
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
transition: all 0.3s ease;
|
||||
min-height: 160px;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
.compact-card:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 1.5rem;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Цветовые схемы для иконок */
|
||||
.primary-icon {
|
||||
background: rgba(13, 110, 253, 0.15);
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
background: rgba(25, 135, 84, 0.15);
|
||||
color: #198754;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
background: rgba(255, 193, 7, 0.15);
|
||||
color: #ffc107;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
background: rgba(23, 162, 184, 0.15);
|
||||
color: #17a2b8;
|
||||
}
|
||||
|
||||
.danger-icon {
|
||||
background: rgba(220, 53, 69, 0.15);
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.secondary-icon {
|
||||
background: rgba(108, 117, 125, 0.15);
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.stock-icon {
|
||||
background: rgba(13, 202, 240, 0.15);
|
||||
color: #0dcaf0;
|
||||
}
|
||||
|
||||
.batch-icon {
|
||||
background: rgba(111, 66, 193, 0.15);
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.journal-icon {
|
||||
background: rgba(253, 126, 20, 0.15);
|
||||
color: #fd7e14;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
color: #6c757d;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.compact-card:hover .btn-text {
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
/* Легкий фон для вызва категорий */
|
||||
.text-uppercase {
|
||||
letter-spacing: 1px;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.compact-card {
|
||||
min-height: 140px;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Массовое поступление товара{% endblock %}
|
||||
{% block inventory_content %}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}Отмена приходу товара{% endblock %}
|
||||
|
||||
@@ -23,8 +24,8 @@
|
||||
<ul class="mb-0">
|
||||
<li><strong>Товар:</strong> {{ incoming.product.name }}</li>
|
||||
<li><strong>Склад:</strong> {{ incoming.warehouse.name }}</li>
|
||||
<li><strong>Количество:</strong> {{ incoming.quantity }} шт</li>
|
||||
<li><strong>Цена закупки:</strong> {{ incoming.cost_price }} ₽</li>
|
||||
<li><strong>Количество:</strong> {{ incoming.quantity|smart_quantity }} шт</li>
|
||||
<li><strong>Цена закупки:</strong> {{ incoming.cost_price }} руб.</li>
|
||||
{% if incoming.document_number %}
|
||||
<li><strong>Номер документа:</strong> {{ incoming.document_number }}</li>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}
|
||||
{% if form.instance.pk %}
|
||||
@@ -55,7 +56,7 @@
|
||||
<label for="{{ form.quantity.id_for_label }}" class="form-label">
|
||||
{{ form.quantity.label }} <span class="text-danger">*</span>
|
||||
</label>
|
||||
{{ form.quantity }}
|
||||
{{ form.quantity|smart_quantity }}
|
||||
{% if form.quantity.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.quantity.errors %}{{ error }}{% endfor %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}История приходов товара{% endblock %}
|
||||
|
||||
@@ -32,8 +33,8 @@
|
||||
<tr>
|
||||
<td><strong>{{ incoming.product.name }}</strong></td>
|
||||
<td>{{ incoming.batch.warehouse.name }}</td>
|
||||
<td>{{ incoming.quantity }} шт</td>
|
||||
<td>{{ incoming.cost_price }} ₽</td>
|
||||
<td>{{ incoming.quantity|smart_quantity }} шт</td>
|
||||
<td>{{ incoming.cost_price }} руб.</td>
|
||||
<td>
|
||||
{% if incoming.batch.document_number %}
|
||||
<code>{{ incoming.batch.document_number }}</code>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Партия {{ batch.document_number }}{% endblock %}
|
||||
{% block inventory_content %}
|
||||
<div class="card">
|
||||
@@ -71,11 +72,11 @@
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td>{{ item.product.name }}</td>
|
||||
<td>{{ item.quantity }}</td>
|
||||
<td>{{ item.cost_price }} ₽</td>
|
||||
<td>{{ item.quantity|smart_quantity }}</td>
|
||||
<td>{{ item.cost_price }} руб.</td>
|
||||
<td>
|
||||
{% widthratio item.quantity 1 item.cost_price as total_price %}
|
||||
<strong>{{ total_price|floatformat:2 }} ₽</strong>
|
||||
<strong>{{ total_price|floatformat:2 }} руб.</strong>
|
||||
</td>
|
||||
<td>
|
||||
{% if item.stock_batch %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}Детали инвентаризации{% endblock %}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}Внесение результатов инвентаризации{% endblock %}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Новое резервирование{% endblock %}
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Резервирование товара</h4></div><div class="card-body"><form method="post">{% csrf_token %}<div class="mb-3"><label class="form-label">{{ form.product.label }} *</label>{{ form.product }}</div><div class="mb-3"><label class="form-label">{{ form.warehouse.label }} *</label>{{ form.warehouse }}</div><div class="mb-3"><label class="form-label">{{ form.quantity.label }} *</label>{{ form.quantity }}</div><div class="mb-3"><label class="form-label">{{ form.order_item.label }}</label>{{ form.order_item }}</div><div class="d-flex gap-2"><button type="submit" class="btn btn-primary">Сохранить</button><a href="{% url 'inventory:reservation-list' %}" class="btn btn-secondary">Отмена</a></div></form></div></div>
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Резервирование товара</h4></div><div class="card-body"><form method="post">{% csrf_token %}<div class="mb-3"><label class="form-label">{{ form.product.label }} *</label>{{ form.product }}</div><div class="mb-3"><label class="form-label">{{ form.warehouse.label }} *</label>{{ form.warehouse }}</div><div class="mb-3"><label class="form-label">{{ form.quantity.label }} *</label>{{ form.quantity|smart_quantity }}</div><div class="mb-3"><label class="form-label">{{ form.order_item.label }}</label>{{ form.order_item }}</div><div class="d-flex gap-2"><button type="submit" class="btn btn-primary">Сохранить</button><a href="{% url 'inventory:reservation-list' %}" class="btn btn-secondary">Отмена</a></div></form></div></div>
|
||||
<style>select,input{width:100%;}</style>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Резервирования{% endblock %}
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Активные резервирования <a href="{% url 'inventory:reservation-create' %}" class="btn btn-primary btn-sm float-end"><i class="bi bi-plus-circle"></i> Новое</a></h4></div><div class="card-body">{% if reservations %}<table class="table table-hover table-sm"><thead><tr><th>Товар</th><th>Кол-во</th><th>Склад</th><th>Зарезервировано</th><th class="text-end">Действия</th></tr></thead><tbody>{% for r in reservations %}<tr><td>{{ r.product.name }}</td><td>{{ r.quantity }}</td><td>{{ r.warehouse.name }}</td><td>{{ r.reserved_at|date:"d.m.Y" }}</td><td class="text-end"><a href="{% url 'inventory:reservation-update' r.pk %}" class="btn btn-sm btn-outline-primary"><i class="bi bi-pencil"></i></a></td></tr>{% endfor %}</tbody></table>{% else %}<div class="alert alert-info">Резервирований не найдено.</div>{% endif %}</div></div>
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Активные резервирования <a href="{% url 'inventory:reservation-create' %}" class="btn btn-primary btn-sm float-end"><i class="bi bi-plus-circle"></i> Новое</a></h4></div><div class="card-body">{% if reservations %}<table class="table table-hover table-sm"><thead><tr><th>Товар</th><th>Кол-во</th><th>Склад</th><th>Зарезервировано</th><th class="text-end">Действия</th></tr></thead><tbody>{% for r in reservations %}<tr><td>{{ r.product.name }}</td><td>{{ r.quantity|smart_quantity }}</td><td>{{ r.warehouse.name }}</td><td>{{ r.reserved_at|date:"d.m.Y" }}</td><td class="text-end"><a href="{% url 'inventory:reservation-update' r.pk %}" class="btn btn-sm btn-outline-primary"><i class="bi bi-pencil"></i></a></td></tr>{% endfor %}</tbody></table>{% else %}<div class="alert alert-info">Резервирований не найдено.</div>{% endif %}</div></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}Отмена продажи{% endblock %}
|
||||
|
||||
@@ -23,8 +24,8 @@
|
||||
<ul class="mb-0">
|
||||
<li><strong>Товар:</strong> {{ sale.product.name }}</li>
|
||||
<li><strong>Склад:</strong> {{ sale.warehouse.name }}</li>
|
||||
<li><strong>Количество:</strong> {{ sale.quantity }} шт</li>
|
||||
<li><strong>Цена продажи:</strong> {{ sale.sale_price }} ₽</li>
|
||||
<li><strong>Количество:</strong> {{ sale.quantity|smart_quantity }} шт</li>
|
||||
<li><strong>Цена продажи:</strong> {{ sale.sale_price }} руб.</li>
|
||||
<li><strong>Статус:</strong>
|
||||
{% if sale.processed %}
|
||||
Обработана
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}Детали продажи{% endblock %}
|
||||
|
||||
@@ -26,15 +27,15 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Количество:</th>
|
||||
<td><strong>{{ sale.quantity }} шт</strong></td>
|
||||
<td><strong>{{ sale.quantity|smart_quantity }} шт</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Цена продажи:</th>
|
||||
<td><strong>{{ sale.sale_price }} ₽</strong></td>
|
||||
<td><strong>{{ sale.sale_price }} руб.</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Сумма:</th>
|
||||
<td><strong>{{ sale.quantity|add:0|multiply:sale.sale_price }} ₽</strong></td>
|
||||
<td><strong>{{ sale.quantity|add:0|multiply:sale.sale_price }} руб.</strong></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -96,16 +97,16 @@
|
||||
<code>Партия #{{ allocation.batch.id }}</code>
|
||||
</td>
|
||||
<td>{{ allocation.batch.created_at|date:"d.m.Y H:i" }}</td>
|
||||
<td>{{ allocation.quantity }} шт</td>
|
||||
<td>{{ allocation.cost_price }} ₽</td>
|
||||
<td><strong>{{ allocation.quantity|add:0|multiply:allocation.cost_price }} ₽</strong></td>
|
||||
<td>{{ allocation.quantity|smart_quantity }} шт</td>
|
||||
<td>{{ allocation.cost_price }} руб.</td>
|
||||
<td><strong>{{ allocation.quantity|add:0|multiply:allocation.cost_price }} руб.</strong></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot class="table-light">
|
||||
<tr>
|
||||
<th colspan="2">Итого:</th>
|
||||
<th>{{ sale.quantity }} шт</th>
|
||||
<th>{{ sale.quantity|smart_quantity }} шт</th>
|
||||
<th colspan="2">
|
||||
<strong>
|
||||
{% comment %} Сумма всех закупочных цен {% endcomment %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}
|
||||
{% if form.instance.pk %}
|
||||
@@ -55,7 +56,7 @@
|
||||
<label for="{{ form.quantity.id_for_label }}" class="form-label">
|
||||
{{ form.quantity.label }} <span class="text-danger">*</span>
|
||||
</label>
|
||||
{{ form.quantity }}
|
||||
{{ form.quantity|smart_quantity }}
|
||||
{% if form.quantity.errors %}
|
||||
<div class="invalid-feedback d-block">
|
||||
{% for error in form.quantity.errors %}{{ error }}{% endfor %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
|
||||
{% block inventory_title %}История продаж{% endblock %}
|
||||
|
||||
@@ -32,8 +33,8 @@
|
||||
<tr>
|
||||
<td><strong>{{ sale.product.name }}</strong></td>
|
||||
<td>{{ sale.warehouse.name }}</td>
|
||||
<td>{{ sale.quantity }} шт</td>
|
||||
<td>{{ sale.sale_price }} ₽</td>
|
||||
<td>{{ sale.quantity|smart_quantity }} шт</td>
|
||||
<td>{{ sale.sale_price }} руб.</td>
|
||||
<td>
|
||||
{% if sale.order %}
|
||||
<code>{{ sale.order.order_number }}</code>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Остатки товара{% endblock %}
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">{{ stock.product.name }} на {{ stock.warehouse.name }}</h4></div><div class="card-body"><table class="table table-borderless"><tr><th>Товар:</th><td><strong>{{ stock.product.name }}</strong></td></tr><tr><th>Склад:</th><td>{{ stock.warehouse.name }}</td></tr><tr><th>Доступно:</th><td><strong>{{ stock.quantity_available }} шт</strong></td></tr><tr><th>Зарезервировано:</th><td>{{ stock.quantity_reserved }} шт</td></tr><tr><th>Свободно:</th><td><strong>{{ stock.quantity_free }} шт</strong></td></tr><tr><th>Последнее обновление:</th><td>{{ stock.updated_at|date:"d.m.Y H:i" }}</td></tr></table><a href="{% url 'inventory:stock-list' %}" class="btn btn-secondary"><i class="bi bi-arrow-left"></i> Вернуться</a></div></div>
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">{{ stock.product.name }} на {{ stock.warehouse.name }}</h4></div><div class="card-body"><table class="table table-borderless"><tr><th>Товар:</th><td><strong>{{ stock.product.name }}</strong></td></tr><tr><th>Склад:</th><td>{{ stock.warehouse.name }}</td></tr><tr><th>Доступно:</th><td><strong>{{ stock.quantity_available|smart_quantity }} шт</strong></td></tr><tr><th>Зарезервировано:</th><td>{{ stock.quantity_reserved|smart_quantity }} шт</td></tr><tr><th>Свободно:</th><td><strong>{{ stock.quantity_free|smart_quantity }} шт</strong></td></tr><tr><th>Последнее обновление:</th><td>{{ stock.updated_at|date:"d.m.Y H:i" }}</td></tr></table><a href="{% url 'inventory:stock-list' %}" class="btn btn-secondary"><i class="bi bi-arrow-left"></i> Вернуться</a></div></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Остатки товаров{% endblock %}
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Остатки на складах</h4></div><div class="card-body">{% if stocks %}<table class="table table-hover table-sm"><thead><tr><th>Товар</th><th>Склад</th><th>Доступно</th><th>Зарезервировано</th><th>Свободно</th><th>Последний обновления</th></tr></thead><tbody>{% for stock in stocks %}<tr><td><a href="{% url 'inventory:stock-detail' stock.pk %}">{{ stock.product.name }}</a></td><td>{{ stock.warehouse.name }}</td><td>{{ stock.quantity_available }}</td><td>{{ stock.quantity_reserved }}</td><td><strong>{{ stock.quantity_free }}</strong></td><td>{{ stock.updated_at|date:"d.m.Y H:i" }}</td></tr>{% endfor %}</tbody></table>{% else %}<div class="alert alert-info">Остатки не найдены.</div>{% endif %}</div></div>
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Остатки на складах</h4></div><div class="card-body">{% if stocks %}<table class="table table-hover table-sm"><thead><tr><th>Товар</th><th>Склад</th><th>Доступно</th><th>Зарезервировано</th><th>Свободно</th><th>Последний обновления</th></tr></thead><tbody>{% for stock in stocks %}<tr><td><a href="{% url 'inventory:stock-detail' stock.pk %}">{{ stock.product.name }}</a></td><td>{{ stock.warehouse.name }}</td><td>{{ stock.quantity_available|smart_quantity }}</td><td>{{ stock.quantity_reserved|smart_quantity }}</td><td><strong>{{ stock.quantity_free|smart_quantity }}</strong></td><td>{{ stock.updated_at|date:"d.m.Y H:i" }}</td></tr>{% endfor %}</tbody></table>{% else %}<div class="alert alert-info">Остатки не найдены.</div>{% endif %}</div></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Перемещение товара{% endblock %}
|
||||
{% block inventory_content %}
|
||||
<div class="card"><div class="card-header"><h4 class="mb-0">Перемещение товара</h4></div><div class="card-body"><form method="post">{% csrf_token %}<div class="mb-3"><label class="form-label">{{ form.batch.label }} *</label>{{ form.batch }}</div><div class="mb-3"><label class="form-label">{{ form.from_warehouse.label }} *</label>{{ form.from_warehouse }}</div><div class="mb-3"><label class="form-label">{{ form.to_warehouse.label }} *</label>{{ form.to_warehouse }}</div><div class="mb-3"><label class="form-label">{{ form.quantity.label }} *</label>{{ form.quantity }}</div><div class="mb-3"><label class="form-label">{{ form.document_number.label }}</label>{{ form.document_number }}</div><div class="d-flex gap-2"><button type="submit" class="btn btn-primary">Сохранить</button><a href="{% url 'inventory:transfer-list' %}" class="btn btn-secondary">Отмена</a></div></form></div></div>
|
||||
<div class="card"><div class="card-header"><h4 class="mb-0">Перемещение товара</h4></div><div class="card-body"><form method="post">{% csrf_token %}<div class="mb-3"><label class="form-label">{{ form.batch.label }} *</label>{{ form.batch }}</div><div class="mb-3"><label class="form-label">{{ form.from_warehouse.label }} *</label>{{ form.from_warehouse }}</div><div class="mb-3"><label class="form-label">{{ form.to_warehouse.label }} *</label>{{ form.to_warehouse }}</div><div class="mb-3"><label class="form-label">{{ form.quantity.label }} *</label>{{ form.quantity|smart_quantity }}</div><div class="mb-3"><label class="form-label">{{ form.document_number.label }}</label>{{ form.document_number }}</div><div class="d-flex gap-2"><button type="submit" class="btn btn-primary">Сохранить</button><a href="{% url 'inventory:transfer-list' %}" class="btn btn-secondary">Отмена</a></div></form></div></div>
|
||||
<style>select,textarea,input{width:100%;}</style>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}Перемещение товаров{% endblock %}
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Перемещение товаров между складами <a href="{% url 'inventory:transfer-create' %}" class="btn btn-primary btn-sm float-end"><i class="bi bi-plus-circle"></i> Новое</a></h4></div><div class="card-body">{% if transfers %}<div class="table-responsive"><table class="table table-hover table-sm"><thead><tr><th>Товар</th><th>Из</th><th>В</th><th>Кол-во</th><th>Дата</th><th class="text-end">Действия</th></tr></thead><tbody>{% for t in transfers %}<tr><td>{{ t.batch.product.name }}</td><td>{{ t.from_warehouse.name }}</td><td>{{ t.to_warehouse.name }}</td><td>{{ t.quantity }}</td><td>{{ t.date|date:"d.m.Y" }}</td><td class="text-end"><a href="{% url 'inventory:transfer-update' t.pk %}" class="btn btn-sm btn-outline-primary"><i class="bi bi-pencil"></i></a><a href="{% url 'inventory:transfer-delete' t.pk %}" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></a></td></tr>{% endfor %}</tbody></table></div>{% else %}<div class="alert alert-info">Перемещений не найдено.</div>{% endif %}</div></div>
|
||||
{% block inventory_content %}<div class="card"><div class="card-header"><h4 class="mb-0">Перемещение товаров между складами <a href="{% url 'inventory:transfer-create' %}" class="btn btn-primary btn-sm float-end"><i class="bi bi-plus-circle"></i> Новое</a></h4></div><div class="card-body">{% if transfers %}<div class="table-responsive"><table class="table table-hover table-sm"><thead><tr><th>Товар</th><th>Из</th><th>В</th><th>Кол-во</th><th>Дата</th><th class="text-end">Действия</th></tr></thead><tbody>{% for t in transfers %}<tr><td>{{ t.batch.product.name }}</td><td>{{ t.from_warehouse.name }}</td><td>{{ t.to_warehouse.name }}</td><td>{{ t.quantity|smart_quantity }}</td><td>{{ t.date|date:"d.m.Y" }}</td><td class="text-end"><a href="{% url 'inventory:transfer-update' t.pk %}" class="btn btn-sm btn-outline-primary"><i class="bi bi-pencil"></i></a><a href="{% url 'inventory:transfer-delete' t.pk %}" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></a></td></tr>{% endfor %}</tbody></table></div>{% else %}<div class="alert alert-info">Перемещений не найдено.</div>{% endif %}</div></div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
|
||||
{% block inventory_title %}Удаление склада{% endblock %}
|
||||
{% block inventory_title %}Архивирование склада{% endblock %}
|
||||
|
||||
{% block inventory_content %}
|
||||
<div class="card border-danger">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h4 class="mb-0">Подтверждение удаления</h4>
|
||||
<div class="card border-warning">
|
||||
<div class="card-header bg-warning text-dark">
|
||||
<h4 class="mb-0">Архивирование склада</h4>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning">
|
||||
<i class="bi bi-exclamation-triangle"></i>
|
||||
<strong>Внимание!</strong> Вы собираетесь удалить (деактивировать) склад.
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i>
|
||||
Вы собираетесь архивировать склад <strong>"{{ warehouse.name }}"</strong>
|
||||
</div>
|
||||
|
||||
<p class="text-muted">
|
||||
Этот склад будет деактивирован и скрыт из основного списка.
|
||||
<p>
|
||||
<strong>Что произойдет после архивирования:</strong>
|
||||
</p>
|
||||
<ul>
|
||||
<li>✓ Склад исчезнет из списка активных складов</li>
|
||||
<li>✓ Новые документы нельзя будет создавать для этого склада</li>
|
||||
<li>✓ Историю операций можно будет посмотреть в архиве</li>
|
||||
</ul>
|
||||
|
||||
<h5>Склад: <strong>{{ warehouse.name }}</strong></h5>
|
||||
<div class="alert alert-secondary mt-3">
|
||||
<small>Вы всегда сможете вернуть склад, отредактировав его позже.</small>
|
||||
</div>
|
||||
|
||||
{% if warehouse.description %}
|
||||
<p class="text-muted">{{ warehouse.description }}</p>
|
||||
@@ -28,8 +35,8 @@
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="bi bi-trash"></i> Подтвердить удаление
|
||||
<button type="submit" class="btn btn-warning">
|
||||
<i class="bi bi-archive"></i> Архивировать
|
||||
</button>
|
||||
<a href="{% url 'inventory:warehouse-list' %}" class="btn btn-secondary">
|
||||
<i class="bi bi-x-circle"></i> Отменить
|
||||
|
||||
@@ -67,6 +67,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" class="form-check-input"
|
||||
id="{{ form.is_default.id_for_label }}" name="{{ form.is_default.html_name }}"
|
||||
{% if form.is_default.value %}checked{% endif %}>
|
||||
<label class="form-check-label" for="{{ form.is_default.id_for_label }}">
|
||||
{{ form.is_default.label }}
|
||||
<small class="text-muted d-block">
|
||||
Отмечьте, чтобы использовать этот склад по умолчанию при создании новых документов
|
||||
</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-check-circle"></i>
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
{% block inventory_title %}Управление складами{% endblock %}
|
||||
|
||||
{% block inventory_content %}
|
||||
<!-- Скрытое поле для CSRF токена (нужно для AJAX запросов) -->
|
||||
<div style="display: none;">
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h4 class="mb-0">Список складов</h4>
|
||||
@@ -17,6 +21,7 @@
|
||||
<table class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width: 40px;">✓</th>
|
||||
<th>Название</th>
|
||||
<th>Описание</th>
|
||||
<th>Статус</th>
|
||||
@@ -26,8 +31,20 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for warehouse in warehouses %}
|
||||
<tr>
|
||||
<td><strong>{{ warehouse.name }}</strong></td>
|
||||
<tr {% if warehouse.is_default %}class="table-warning"{% endif %} data-warehouse-id="{{ warehouse.pk }}">
|
||||
<td class="text-center">
|
||||
<input type="checkbox" class="default-warehouse-checkbox"
|
||||
data-warehouse-id="{{ warehouse.pk }}"
|
||||
data-set-default-url="{% url 'inventory:warehouse-set-default' warehouse.pk %}"
|
||||
{% if warehouse.is_default %}checked{% endif %}
|
||||
style="cursor: pointer; width: 18px; height: 18px;">
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ warehouse.name }}</strong>
|
||||
{% if warehouse.is_default %}
|
||||
<span class="badge bg-warning text-dark ms-2">По умолчанию</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ warehouse.description|truncatewords:10 }}</td>
|
||||
<td>
|
||||
{% if warehouse.is_active %}
|
||||
@@ -95,4 +112,145 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Обработчик для галочек "По умолчанию"
|
||||
const checkboxes = document.querySelectorAll('.default-warehouse-checkbox');
|
||||
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
const warehouseId = this.dataset.warehouseId;
|
||||
const setDefaultUrl = this.dataset.setDefaultUrl;
|
||||
|
||||
// Получаем CSRF токен из скрытого input в форме
|
||||
let csrfToken = document.querySelector('[name=csrfmiddlewaretoken]')?.value;
|
||||
|
||||
// Если токена нет в form, ищем в meta тегах
|
||||
if (!csrfToken) {
|
||||
csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||||
}
|
||||
|
||||
// Если токена еще нет, ищем его в самой странице через Cookies
|
||||
if (!csrfToken) {
|
||||
const name = 'csrftoken';
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
csrfToken = cookieValue;
|
||||
}
|
||||
|
||||
console.log('CSRF Token:', csrfToken ? 'найден (' + csrfToken.length + ' символов)' : 'не найден');
|
||||
|
||||
// Если галочка установлена, отправляем запрос
|
||||
if (this.checked) {
|
||||
// Визуально обновляем таблицу сразу (оптимистичное обновление)
|
||||
document.querySelectorAll('input.default-warehouse-checkbox').forEach(cb => {
|
||||
cb.checked = false;
|
||||
});
|
||||
document.querySelectorAll('tr[data-warehouse-id]').forEach(tr => {
|
||||
tr.classList.remove('table-warning');
|
||||
tr.querySelector('.badge.bg-warning')?.remove();
|
||||
});
|
||||
|
||||
// Отмечаем текущую строку
|
||||
this.checked = true;
|
||||
const currentRow = document.querySelector(`tr[data-warehouse-id="${warehouseId}"]`);
|
||||
currentRow.classList.add('table-warning');
|
||||
|
||||
// Добавляем бейдж "По умолчанию" если его нет
|
||||
const nameCell = currentRow.querySelector('td:nth-child(2)');
|
||||
if (!nameCell.querySelector('.badge.bg-warning')) {
|
||||
const badge = document.createElement('span');
|
||||
badge.className = 'badge bg-warning text-dark ms-2';
|
||||
badge.textContent = 'По умолчанию';
|
||||
nameCell.appendChild(badge);
|
||||
}
|
||||
|
||||
// Отправляем AJAX запрос на правильный URL из атрибута data
|
||||
console.log('Отправляем запрос на:', setDefaultUrl);
|
||||
console.log('С заголовками:', {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': csrfToken ? '***' + csrfToken.slice(-10) : 'не найден'
|
||||
});
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
// Добавляем CSRF токен если он найден
|
||||
if (csrfToken) {
|
||||
headers['X-CSRFToken'] = csrfToken;
|
||||
}
|
||||
|
||||
fetch(setDefaultUrl, {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify({})
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Ответ сервера:', response.status);
|
||||
if (!response.ok) {
|
||||
return response.text().then(text => {
|
||||
throw new Error(`HTTP ${response.status}: ${text}`);
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Данные:', data);
|
||||
if (data.status === 'success') {
|
||||
console.log(data.message);
|
||||
// Показываем уведомление
|
||||
showNotification(data.message, 'success');
|
||||
} else {
|
||||
throw new Error(data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Ошибка при запросе:', error);
|
||||
// Откатываем визуальные изменения при ошибке
|
||||
showNotification('Ошибка при установке склада по умолчанию: ' + error.message, 'error');
|
||||
// Перезагружаем через 2 секунды
|
||||
setTimeout(() => {
|
||||
location.reload();
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Функция для показа уведомлений
|
||||
function showNotification(message, type = 'info') {
|
||||
const alertClass = type === 'success' ? 'alert-success' : 'alert-danger';
|
||||
const alertHtml = `
|
||||
<div class="alert ${alertClass} alert-dismissible fade show" role="alert">
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const cardBody = document.querySelector('.card-body');
|
||||
const alertElement = document.createElement('div');
|
||||
alertElement.innerHTML = alertHtml;
|
||||
cardBody.insertBefore(alertElement.firstElementChild, cardBody.firstChild);
|
||||
|
||||
// Автоматически скрываем через 4 секунды
|
||||
setTimeout(() => {
|
||||
const alert = cardBody.querySelector('.alert');
|
||||
if (alert) {
|
||||
alert.remove();
|
||||
}
|
||||
}, 4000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}{% if form.instance.pk %}Редактирование списания{% else %}Новое списание{% endif %}{% endblock %}
|
||||
{% block inventory_content %}
|
||||
<div class="card">
|
||||
@@ -36,7 +37,7 @@
|
||||
<!-- Поле Количество -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">{{ form.quantity.label }} <span class="text-danger">*</span></label>
|
||||
{{ form.quantity }}
|
||||
{{ form.quantity|smart_quantity }}
|
||||
{% if form.quantity.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.quantity.errors.0 }}</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{% extends 'inventory/base_inventory.html' %}
|
||||
{% load inventory_filters %}
|
||||
{% block inventory_title %}История списаний{% endblock %}
|
||||
{% block inventory_content %}
|
||||
<div class="card">
|
||||
@@ -25,7 +26,7 @@
|
||||
{% for writeoff in writeoffs %}
|
||||
<tr>
|
||||
<td><strong>{{ writeoff.batch.product.name }}</strong></td>
|
||||
<td>{{ writeoff.quantity }} шт</td>
|
||||
<td>{{ writeoff.quantity|smart_quantity }} шт</td>
|
||||
<td><span class="badge bg-warning">{{ writeoff.get_reason_display }}</span></td>
|
||||
<td>{{ writeoff.date|date:"d.m.Y H:i" }}</td>
|
||||
<td class="text-end">
|
||||
|
||||
Reference in New Issue
Block a user