- Исправлены комментарии и форматирование в signals.py - Улучшена читаемость кода в models.py - Обновлены шаблоны форм статусов - Доработаны тесты переходов статусов
317 lines
16 KiB
HTML
317 lines
16 KiB
HTML
{% extends "base.html" %}
|
||
{% load static %}
|
||
|
||
{% block title %}{% if form.instance.pk %}Редактировать{% else %}Создать{% endif %} статус{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container-fluid mt-4">
|
||
<div class="row mb-4">
|
||
<div class="col-md-8">
|
||
<h1>
|
||
{% if form.instance.pk %}
|
||
Редактировать статус: {{ form.instance.name }}
|
||
{% else %}
|
||
Создать новый статус
|
||
{% endif %}
|
||
</h1>
|
||
{% if is_system %}
|
||
<div class="alert alert-danger mt-2">
|
||
<i class="fas fa-exclamation-triangle"></i>
|
||
<strong>ВНИМАНИЕ!</strong> Это системный статус, используемый в бизнес-логике системы.
|
||
<ul class="mb-0 mt-2">
|
||
<li><strong>Код статуса</strong> — заблокирован (используется в коде для проверок)</li>
|
||
<li><strong>Положительный/Отрицательный исход</strong> — заблокированы (управляют резервированием и списанием товаров со склада)</li>
|
||
<li>Разрешено изменять: название, метку, цвет и описание</li>
|
||
</ul>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
<div class="col-md-4 text-end">
|
||
<a href="{% url 'orders:status_list' %}" class="btn btn-secondary">
|
||
<i class="fas fa-arrow-left"></i> Вернуться к статусам
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
{% if form.non_field_errors %}
|
||
<div class="alert alert-danger" role="alert">
|
||
<h4 class="alert-heading">Ошибка!</h4>
|
||
{% for error in form.non_field_errors %}
|
||
<p>{{ error }}</p>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="row">
|
||
<div class="col-md-8">
|
||
<div class="card">
|
||
<div class="card-body">
|
||
<form method="post" novalidate>
|
||
{% csrf_token %}
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label for="{{ form.name.id_for_label }}" class="form-label">
|
||
{{ form.name.label }}
|
||
<span class="text-danger">*</span>
|
||
</label>
|
||
{{ form.name }}
|
||
{% if form.name.errors %}
|
||
<div class="invalid-feedback d-block">
|
||
{% for error in form.name.errors %}
|
||
{{ error }}
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
<small class="form-text text-muted">
|
||
Например: Выполнен, В процессе, Возврат
|
||
</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label for="{{ form.code.id_for_label }}" class="form-label">
|
||
{{ form.code.label }}
|
||
<span class="text-danger">*</span>
|
||
</label>
|
||
{{ form.code }}
|
||
{% if form.code.errors %}
|
||
<div class="invalid-feedback d-block">
|
||
{% for error in form.code.errors %}
|
||
{{ error }}
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
<small class="form-text text-muted">
|
||
{% if form.code.field.help_text %}
|
||
{{ form.code.field.help_text|safe }}
|
||
{% else %}
|
||
Латинские буквы, цифры и подчеркивания
|
||
{% endif %}
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label for="{{ form.label.id_for_label }}" class="form-label">
|
||
{{ form.label.label }}
|
||
</label>
|
||
{{ form.label }}
|
||
{% if form.label.errors %}
|
||
<div class="invalid-feedback d-block">
|
||
{% for error in form.label.errors %}
|
||
{{ error }}
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
<small class="form-text text-muted">
|
||
Для отображения в интерфейсе (опционально)
|
||
</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label for="{{ form.color.id_for_label }}" class="form-label">
|
||
{{ form.color.label }}
|
||
</label>
|
||
<div class="input-group">
|
||
{{ form.color }}
|
||
<span class="input-group-text" id="color-preview" style="width: 60px; background-color: #808080;"></span>
|
||
</div>
|
||
{% if form.color.errors %}
|
||
<div class="invalid-feedback d-block">
|
||
{% for error in form.color.errors %}
|
||
{{ error }}
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="{{ form.description.id_for_label }}" class="form-label">
|
||
{{ form.description.label }}
|
||
</label>
|
||
{{ form.description }}
|
||
{% if form.description.errors %}
|
||
<div class="invalid-feedback d-block">
|
||
{% for error in form.description.errors %}
|
||
{{ error }}
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
<small class="form-text text-muted">
|
||
Описание для пользователей (опционально)
|
||
</small>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div class="form-check mb-3">
|
||
{{ form.is_positive_end }}
|
||
<label class="form-check-label" for="{{ form.is_positive_end.id_for_label }}">
|
||
{{ form.is_positive_end.label }}
|
||
</label>
|
||
<small class="d-block text-muted mt-1">
|
||
Отметьте, если это успешный финальный статус (Выполнен)
|
||
</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6">
|
||
<div class="form-check mb-3">
|
||
{{ form.is_negative_end }}
|
||
<label class="form-check-label" for="{{ form.is_negative_end.id_for_label }}">
|
||
{{ form.is_negative_end.label }}
|
||
</label>
|
||
<small class="d-block text-muted mt-1">
|
||
Отметьте, если это отрицательный финальный статус (Отменен)
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="d-flex gap-2 mt-4">
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fas fa-save"></i>
|
||
{% if form.instance.pk %}
|
||
Сохранить изменения
|
||
{% else %}
|
||
Создать статус
|
||
{% endif %}
|
||
</button>
|
||
<a href="{% url 'orders:status_list' %}" class="btn btn-secondary">
|
||
<i class="fas fa-times"></i> Отменить
|
||
</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-4">
|
||
<div class="card">
|
||
<div class="card-header bg-light">
|
||
<h6 class="mb-0">Предпросмотр</h6>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="mb-3">
|
||
<label class="form-label">Название статуса</label>
|
||
<p class="lead">
|
||
<span id="preview-name">{{ form.instance.name|default:"Название" }}</span>
|
||
</p>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Код статуса</label>
|
||
<code id="preview-code">{{ form.instance.code|default:"код" }}</code>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Внешний вид</label>
|
||
<div style="padding: 10px; border-radius: 4px; background-color: {{ form.instance.color|default:'#808080' }}; color: white; text-align: center;" id="preview-color">
|
||
<strong id="preview-color-text">{{ form.instance.name|default:"Статус" }}</strong>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Тип статуса</label>
|
||
<p id="preview-type">
|
||
{% if form.instance.is_system %}
|
||
<span class="badge bg-info">Системный</span>
|
||
{% else %}
|
||
<span class="badge bg-success">Пользовательский</span>
|
||
{% endif %}
|
||
</p>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label">Финальный статус</label>
|
||
<p id="preview-end">
|
||
{% if form.instance.is_positive_end %}
|
||
<span class="badge bg-success">✓ Успешный исход сделки</span>
|
||
{% elif form.instance.is_negative_end %}
|
||
<span class="badge bg-danger">✗ Отрицательный исход сделки</span>
|
||
{% else %}
|
||
<span class="badge bg-secondary">Промежуточный</span>
|
||
{% endif %}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const nameInput = document.getElementById('{{ form.name.id_for_label }}');
|
||
const codeInput = document.getElementById('{{ form.code.id_for_label }}');
|
||
const colorInput = document.getElementById('{{ form.color.id_for_label }}');
|
||
const positiveEndCheckbox = document.getElementById('{{ form.is_positive_end.id_for_label }}');
|
||
const negativeEndCheckbox = document.getElementById('{{ form.is_negative_end.id_for_label }}');
|
||
|
||
const previewName = document.getElementById('preview-name');
|
||
const previewCode = document.getElementById('preview-code');
|
||
const previewColor = document.getElementById('preview-color');
|
||
const previewColorText = document.getElementById('preview-color-text');
|
||
const colorPreview = document.getElementById('color-preview');
|
||
const previewEnd = document.getElementById('preview-end');
|
||
|
||
function updatePreview() {
|
||
// Обновляем название
|
||
previewName.textContent = nameInput.value || 'Название';
|
||
previewColorText.textContent = nameInput.value || 'Статус';
|
||
|
||
// Обновляем код
|
||
previewCode.textContent = codeInput.value || 'код';
|
||
|
||
// Обновляем цвет
|
||
const color = colorInput.value || '#808080';
|
||
previewColor.style.backgroundColor = color;
|
||
colorPreview.style.backgroundColor = color;
|
||
|
||
// Обновляем тип конца
|
||
if (positiveEndCheckbox.checked) {
|
||
previewEnd.innerHTML = '<span class="badge bg-success">✓ Успешный исход сделки</span>';
|
||
} else if (negativeEndCheckbox.checked) {
|
||
previewEnd.innerHTML = '<span class="badge bg-danger">✗ Отрицательный исход сделки</span>';
|
||
} else {
|
||
previewEnd.innerHTML = '<span class="badge bg-secondary">Промежуточный</span>';
|
||
}
|
||
}
|
||
|
||
nameInput.addEventListener('input', updatePreview);
|
||
codeInput.addEventListener('input', updatePreview);
|
||
colorInput.addEventListener('change', updatePreview);
|
||
positiveEndCheckbox.addEventListener('change', updatePreview);
|
||
negativeEndCheckbox.addEventListener('change', updatePreview);
|
||
|
||
// Взаимное отключение чекбоксов: нельзя быть одновременно положительным и отрицательным
|
||
positiveEndCheckbox.addEventListener('change', function() {
|
||
if (this.checked) {
|
||
negativeEndCheckbox.checked = false;
|
||
}
|
||
updatePreview();
|
||
});
|
||
|
||
negativeEndCheckbox.addEventListener('change', function() {
|
||
if (this.checked) {
|
||
positiveEndCheckbox.checked = false;
|
||
}
|
||
updatePreview();
|
||
});
|
||
|
||
// Инициальное обновление
|
||
updatePreview();
|
||
});
|
||
</script>
|
||
{% endblock %}
|