Add default showcase selection per warehouse
- Add is_default field to Showcase model with unique constraint per warehouse - Implement Showcase.save() to ensure only one default per warehouse - Add SetDefaultShowcaseView for AJAX-based default selection - Update ShowcaseForm to include is_default checkbox - Add interactive checkbox UI in showcase list with AJAX functionality - Update POS API to return showcase.is_default instead of warehouse.is_default - Update terminal.js to auto-select showcase based on its is_default flag - Add migration for is_default field 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ class ShowcaseForm(forms.ModelForm):
|
|||||||
"""
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Showcase
|
model = Showcase
|
||||||
fields = ['name', 'warehouse', 'description', 'is_active']
|
fields = ['name', 'warehouse', 'description', 'is_active', 'is_default']
|
||||||
widgets = {
|
widgets = {
|
||||||
'name': forms.TextInput(attrs={
|
'name': forms.TextInput(attrs={
|
||||||
'class': 'form-control',
|
'class': 'form-control',
|
||||||
@@ -25,16 +25,19 @@ class ShowcaseForm(forms.ModelForm):
|
|||||||
'placeholder': 'Описание витрины, её расположение или особенности'
|
'placeholder': 'Описание витрины, её расположение или особенности'
|
||||||
}),
|
}),
|
||||||
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||||||
|
'is_default': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
'name': 'Название витрины',
|
'name': 'Название витрины',
|
||||||
'warehouse': 'Склад',
|
'warehouse': 'Склад',
|
||||||
'description': 'Описание',
|
'description': 'Описание',
|
||||||
'is_active': 'Активна',
|
'is_active': 'Активна',
|
||||||
|
'is_default': 'Витрина по умолчанию',
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'warehouse': 'Склад, к которому привязана витрина',
|
'warehouse': 'Склад, к которому привязана витрина',
|
||||||
'is_active': 'Неактивные витрины скрыты из списка выбора',
|
'is_active': 'Неактивные витрины скрыты из списка выбора',
|
||||||
|
'is_default': 'Витрина будет автоматически выбрана при создании резерва на этом складе',
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 5.0.10 on 2025-11-20 08:08
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0003_showcase_reservation_showcase_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='showcase',
|
||||||
|
name='is_default',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='По умолчанию'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='showcase',
|
||||||
|
index=models.Index(fields=['is_default'], name='inventory_s_is_defa_bf9a7c_idx'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -369,6 +369,7 @@ class Showcase(models.Model):
|
|||||||
related_name='showcases', verbose_name="Склад")
|
related_name='showcases', verbose_name="Склад")
|
||||||
description = models.TextField(blank=True, null=True, verbose_name="Описание")
|
description = models.TextField(blank=True, null=True, verbose_name="Описание")
|
||||||
is_active = models.BooleanField(default=True, verbose_name="Активна")
|
is_active = models.BooleanField(default=True, verbose_name="Активна")
|
||||||
|
is_default = models.BooleanField(default=False, verbose_name="По умолчанию")
|
||||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
|
||||||
updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления")
|
updated_at = models.DateTimeField(auto_now=True, verbose_name="Дата обновления")
|
||||||
|
|
||||||
@@ -379,11 +380,19 @@ class Showcase(models.Model):
|
|||||||
indexes = [
|
indexes = [
|
||||||
models.Index(fields=['warehouse']),
|
models.Index(fields=['warehouse']),
|
||||||
models.Index(fields=['is_active']),
|
models.Index(fields=['is_active']),
|
||||||
|
models.Index(fields=['is_default']),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.name} ({self.warehouse.name})"
|
return f"{self.name} ({self.warehouse.name})"
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
"""Обеспечиваем что только одна витрина может быть по умолчанию для каждого склада"""
|
||||||
|
if self.is_default:
|
||||||
|
# Снимаем флаг is_default со всех других витрин этого склада
|
||||||
|
Showcase.objects.filter(warehouse=self.warehouse, is_default=True).exclude(pk=self.pk).update(is_default=False)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Reservation(models.Model):
|
class Reservation(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Is Active -->
|
<!-- Is Active -->
|
||||||
<div class="mb-4">
|
<div class="mb-3">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
{{ form.is_active }}
|
{{ form.is_active }}
|
||||||
<label class="form-check-label" for="{{ form.is_active.id_for_label }}">
|
<label class="form-check-label" for="{{ form.is_active.id_for_label }}">
|
||||||
@@ -102,6 +102,24 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Is Default -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<div class="form-check">
|
||||||
|
{{ form.is_default }}
|
||||||
|
<label class="form-check-label" for="{{ form.is_default.id_for_label }}">
|
||||||
|
{{ form.is_default.label }}
|
||||||
|
</label>
|
||||||
|
{% if form.is_default.help_text %}
|
||||||
|
<div><small class="form-text text-muted">{{ form.is_default.help_text }}</small></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% if form.is_default.errors %}
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{ form.is_default.errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
<!-- Скрытое поле для CSRF токена (нужно для AJAX запросов) -->
|
||||||
|
<div style="display: none;">
|
||||||
|
{% csrf_token %}
|
||||||
|
</div>
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
@@ -77,6 +81,7 @@
|
|||||||
<table class="table table-hover align-middle">
|
<table class="table table-hover align-middle">
|
||||||
<thead class="table-light">
|
<thead class="table-light">
|
||||||
<tr>
|
<tr>
|
||||||
|
<th style="width: 40px;">✓</th>
|
||||||
<th>Название</th>
|
<th>Название</th>
|
||||||
<th>Склад</th>
|
<th>Склад</th>
|
||||||
<th>Описание</th>
|
<th>Описание</th>
|
||||||
@@ -90,15 +95,26 @@
|
|||||||
{% for warehouse_group in showcases_by_warehouse %}
|
{% for warehouse_group in showcases_by_warehouse %}
|
||||||
<!-- Warehouse Header Row -->
|
<!-- Warehouse Header Row -->
|
||||||
<tr class="table-secondary">
|
<tr class="table-secondary">
|
||||||
<td colspan="6" class="fw-bold">
|
<td colspan="7" class="fw-bold">
|
||||||
<i class="bi bi-building me-2"></i>{{ warehouse_group.grouper.name }}
|
<i class="bi bi-building me-2"></i>{{ warehouse_group.grouper.name }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- Showcases in this warehouse -->
|
<!-- Showcases in this warehouse -->
|
||||||
{% for showcase in warehouse_group.list %}
|
{% for showcase in warehouse_group.list %}
|
||||||
<tr>
|
<tr {% if showcase.is_default %}class="table-warning"{% endif %} data-showcase-id="{{ showcase.pk }}" data-warehouse-id="{{ showcase.warehouse.id }}">
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox" class="default-showcase-checkbox"
|
||||||
|
data-showcase-id="{{ showcase.pk }}"
|
||||||
|
data-warehouse-id="{{ showcase.warehouse.id }}"
|
||||||
|
data-set-default-url="{% url 'inventory:showcase-set-default' showcase.pk %}"
|
||||||
|
{% if showcase.is_default %}checked{% endif %}
|
||||||
|
style="cursor: pointer; width: 18px; height: 18px;">
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<strong>{{ showcase.name }}</strong>
|
<strong>{{ showcase.name }}</strong>
|
||||||
|
{% if showcase.is_default %}
|
||||||
|
<span class="badge bg-warning text-dark ms-2">По умолчанию</span>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="text-muted">{{ showcase.warehouse.name }}</span>
|
<span class="text-muted">{{ showcase.warehouse.name }}</span>
|
||||||
@@ -159,4 +175,132 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Обработчик для галочек "По умолчанию"
|
||||||
|
const checkboxes = document.querySelectorAll('.default-showcase-checkbox');
|
||||||
|
|
||||||
|
checkboxes.forEach(checkbox => {
|
||||||
|
checkbox.addEventListener('change', function() {
|
||||||
|
const showcaseId = this.dataset.showcaseId;
|
||||||
|
const warehouseId = this.dataset.warehouseId;
|
||||||
|
const setDefaultUrl = this.dataset.setDefaultUrl;
|
||||||
|
|
||||||
|
// Получаем CSRF токен
|
||||||
|
let csrfToken = document.querySelector('[name=csrfmiddlewaretoken]')?.value;
|
||||||
|
|
||||||
|
if (!csrfToken) {
|
||||||
|
csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если галочка установлена, отправляем запрос
|
||||||
|
if (this.checked) {
|
||||||
|
// Визуально обновляем таблицу сразу (оптимистичное обновление)
|
||||||
|
// Снимаем флаги только с витрин того же склада
|
||||||
|
document.querySelectorAll(`tr[data-warehouse-id="${warehouseId}"]`).forEach(tr => {
|
||||||
|
const cb = tr.querySelector('.default-showcase-checkbox');
|
||||||
|
if (cb) {
|
||||||
|
cb.checked = false;
|
||||||
|
}
|
||||||
|
tr.classList.remove('table-warning');
|
||||||
|
tr.querySelector('.badge.bg-warning')?.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Отмечаем текущую строку
|
||||||
|
this.checked = true;
|
||||||
|
const currentRow = document.querySelector(`tr[data-showcase-id="${showcaseId}"]`);
|
||||||
|
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 запрос
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (csrfToken) {
|
||||||
|
headers['X-CSRFToken'] = csrfToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(setDefaultUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: headers,
|
||||||
|
body: JSON.stringify({})
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
return response.text().then(text => {
|
||||||
|
throw new Error(`HTTP ${response.status}: ${text}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === 'success') {
|
||||||
|
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 container = document.querySelector('.container-fluid');
|
||||||
|
const alertElement = document.createElement('div');
|
||||||
|
alertElement.innerHTML = alertHtml;
|
||||||
|
container.insertBefore(alertElement.firstElementChild, container.firstChild);
|
||||||
|
|
||||||
|
// Автоматически скрываем через 4 секунды
|
||||||
|
setTimeout(() => {
|
||||||
|
const alert = container.querySelector('.alert');
|
||||||
|
if (alert) {
|
||||||
|
alert.remove();
|
||||||
|
}
|
||||||
|
}, 4000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from .views import (
|
|||||||
StockMovementListView,
|
StockMovementListView,
|
||||||
)
|
)
|
||||||
# Showcase views
|
# Showcase views
|
||||||
from .views.showcase import ShowcaseListView, ShowcaseCreateView, ShowcaseUpdateView, ShowcaseDeleteView
|
from .views.showcase import ShowcaseListView, ShowcaseCreateView, ShowcaseUpdateView, ShowcaseDeleteView, SetDefaultShowcaseView
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
app_name = 'inventory'
|
app_name = 'inventory'
|
||||||
@@ -103,4 +103,5 @@ urlpatterns = [
|
|||||||
path('showcases/create/', ShowcaseCreateView.as_view(), name='showcase-create'),
|
path('showcases/create/', ShowcaseCreateView.as_view(), name='showcase-create'),
|
||||||
path('showcases/<int:pk>/edit/', ShowcaseUpdateView.as_view(), name='showcase-update'),
|
path('showcases/<int:pk>/edit/', ShowcaseUpdateView.as_view(), name='showcase-update'),
|
||||||
path('showcases/<int:pk>/delete/', ShowcaseDeleteView.as_view(), name='showcase-delete'),
|
path('showcases/<int:pk>/delete/', ShowcaseDeleteView.as_view(), name='showcase-delete'),
|
||||||
|
path('showcases/<int:pk>/set-default/', SetDefaultShowcaseView.as_view(), name='showcase-set-default'),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.db.models import Count, Q
|
from django.db.models import Count, Q
|
||||||
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
|
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, View
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.http import JsonResponse
|
||||||
|
|
||||||
from inventory.models import Showcase, Reservation
|
from inventory.models import Showcase, Reservation
|
||||||
from inventory.forms_showcase import ShowcaseForm
|
from inventory.forms_showcase import ShowcaseForm
|
||||||
@@ -173,3 +175,42 @@ class ShowcaseDeleteView(DeleteView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
class SetDefaultShowcaseView(LoginRequiredMixin, View):
|
||||||
|
"""
|
||||||
|
Установка витрины по умолчанию для её склада.
|
||||||
|
Обрабатывает POST запрос от AJAX и возвращает JSON ответ.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def post(self, request, pk):
|
||||||
|
"""
|
||||||
|
Установить витрину с заданным pk как витрину по умолчанию для её склада
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
showcase = get_object_or_404(Showcase, pk=pk, is_active=True)
|
||||||
|
|
||||||
|
# Установить эту витрину как по умолчанию
|
||||||
|
# (метод save() в модели автоматически снимет флаг с других витрин этого склада)
|
||||||
|
showcase.is_default = True
|
||||||
|
showcase.save()
|
||||||
|
|
||||||
|
return JsonResponse({
|
||||||
|
'status': 'success',
|
||||||
|
'message': f'Витрина "{showcase.name}" на складе "{showcase.warehouse.name}" установлена по умолчанию',
|
||||||
|
'showcase_id': showcase.id,
|
||||||
|
'showcase_name': showcase.name,
|
||||||
|
'warehouse_id': showcase.warehouse.id,
|
||||||
|
'warehouse_name': showcase.warehouse.name
|
||||||
|
})
|
||||||
|
except Showcase.DoesNotExist:
|
||||||
|
return JsonResponse({
|
||||||
|
'status': 'error',
|
||||||
|
'message': 'Витрина не найдена'
|
||||||
|
}, status=404)
|
||||||
|
except Exception as e:
|
||||||
|
return JsonResponse({
|
||||||
|
'status': 'error',
|
||||||
|
'message': f'Ошибка при установке витрины по умолчанию: {str(e)}'
|
||||||
|
}, status=500)
|
||||||
|
|||||||
@@ -932,20 +932,20 @@ async function loadShowcases() {
|
|||||||
|
|
||||||
if (data.success && data.showcases.length > 0) {
|
if (data.success && data.showcases.length > 0) {
|
||||||
let defaultShowcaseId = null;
|
let defaultShowcaseId = null;
|
||||||
|
|
||||||
data.showcases.forEach(showcase => {
|
data.showcases.forEach(showcase => {
|
||||||
const option = document.createElement('option');
|
const option = document.createElement('option');
|
||||||
option.value = showcase.id;
|
option.value = showcase.id;
|
||||||
option.textContent = `${showcase.name} (${showcase.warehouse_name})`;
|
option.textContent = `${showcase.name} (${showcase.warehouse_name})`;
|
||||||
select.appendChild(option);
|
select.appendChild(option);
|
||||||
|
|
||||||
// Запоминаем витрину склада по умолчанию
|
// Запоминаем витрину по умолчанию
|
||||||
if (showcase.is_default_warehouse) {
|
if (showcase.is_default) {
|
||||||
defaultShowcaseId = showcase.id;
|
defaultShowcaseId = showcase.id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Автовыбор витрины склада по умолчанию
|
// Автовыбор витрины по умолчанию
|
||||||
if (defaultShowcaseId) {
|
if (defaultShowcaseId) {
|
||||||
select.value = defaultShowcaseId;
|
select.value = defaultShowcaseId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -366,15 +366,15 @@ def get_showcases_api(request):
|
|||||||
Используется для выбора витрины при создании временного комплекта.
|
Используется для выбора витрины при создании временного комплекта.
|
||||||
"""
|
"""
|
||||||
showcases = Showcase.objects.filter(is_active=True).select_related('warehouse')
|
showcases = Showcase.objects.filter(is_active=True).select_related('warehouse')
|
||||||
|
|
||||||
showcases_data = [{
|
showcases_data = [{
|
||||||
'id': s.id,
|
'id': s.id,
|
||||||
'name': s.name,
|
'name': s.name,
|
||||||
'warehouse_name': s.warehouse.name,
|
'warehouse_name': s.warehouse.name,
|
||||||
'warehouse_id': s.warehouse.id,
|
'warehouse_id': s.warehouse.id,
|
||||||
'is_default_warehouse': s.warehouse.is_default # Для автовыбора
|
'is_default': s.is_default # Флаг витрины по умолчанию для автовыбора
|
||||||
} for s in showcases]
|
} for s in showcases]
|
||||||
|
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'success': True,
|
'success': True,
|
||||||
'showcases': showcases_data
|
'showcases': showcases_data
|
||||||
|
|||||||
Reference in New Issue
Block a user