# ПРАВИЛА КАБИНЕТА ФУЛФИЛМЕНТА (FULFILLMENT) > ⚠️ **ВАЖНО**: Это файл с техническими деталями кабинета фулфилмента. > Общие бизнес-правила находятся в **[rules-complete.md](./rules-complete.md)** ## Когда использовать этот файл: - Работа с компонентами `/warehouse`, `/fulfillment-supplies`, `/services`, `/employees` - GraphQL запросы для фулфилмента - UI/UX специфика кабинета фулфилмента - Технические детали реализации услуг и логистики ## 1. 🏭 СТРУКТУРА КАБИНЕТА ФУЛФИЛМЕНТА ### 1.1 Основные разделы **ФУЛФИЛМЕНТ (`FULFILLMENT`)** имеет доступ к следующим разделам: - **Склад** (`/warehouse`) - управление товарами по модулям - **Поставки** (`/fulfillment-supplies`) - обработка поставок - **Услуги** (`/services`) - управление услугами и расходниками - **Сотрудники** (`/employees`) - управление персоналом - **Статистика** (`/fulfillment-statistics`) - аналитика фулфилмента - **Партнеры** (`/partners`) - управление контрагентами - **Мессенджер** (`/messenger`) - связь с партнерами - **Настройки** (`/settings`) - профиль и настройки - **Экономика** (`/economics`) - финансовая аналитика ### 1.2 Навигация и роутинг #### При входе в систему: ```typescript switch (user?.organization?.type) { case 'FULFILLMENT': router.push('/warehouse') // Направляем на склад break } ``` #### Специальная логика роутинга: - **МАКСИМАЛЬНЫЕ ПРАВА**: Доступ ко ВСЕМ разделам системы - **АДАПТИВНАЯ НАВИГАЦИЯ**: Sidebar изменяется в зависимости от типа организации - **СПЕЦИАЛЬНЫЙ РОУТИНГ**: `/fulfillment-supplies` вместо `/supplies` > 📖 **Бизнес-логика роутинга**: См. [rules-complete.md#4-система-ролей-и-доступов](./rules-complete.md#4--система-ролей-и-доступов) ## 2. 🎨 UI/UX КОМПОНЕНТЫ ### 2.1 Dashboard компоненты #### Основные компоненты кабинета: - `FulfillmentWarehouseDashboard` - склад по модулям - `FulfillmentSuppliesDashboard` - поставки фулфилмента - `ServicesManagement` - управление услугами - `FulfillmentStatistics` - статистика и аналитика - `EmployeeManagement` - управление сотрудниками #### Wrapper-компоненты: - `HomePageWrapper` - маршрутизация по типам организаций - `EconomicsPageWrapper` - адаптивная экономика по кабинетам #### Специализированные компоненты: - `WarehouseModuleCard` - карточка модуля склада - `ServiceCard` - карточка услуги - `EmployeeCard` - карточка сотрудника - `FulfillmentStats` - статистика фулфилмента - `LogisticsRouteCard` - карточка маршрута доставки ### 2.2 Архитектурные особенности #### Технические правила отображения: ```tsx // GraphQL проверки const { data } = useQuery(GET_FULFILLMENT_DATA, { skip: user?.organization?.type !== 'FULFILLMENT' }) // Условное отображение {user?.organization?.type === "FULFILLMENT" && ( )} // Адаптивные отступы const margin = getSidebarMargin() // Полинг данных для реального времени const { data } = useQuery(GET_FULFILLMENT_STATS, { pollInterval: 60000 // Обновление каждую минуту }) ``` ### 2.3 Система учета склада (3-уровневая иерархия) #### Структура данных: ``` 🔵 УРОВЕНЬ 1: МАГАЗИНЫ ↓ 🟢 УРОВЕНЬ 2: ТОВАРЫ (зеленый - green-500) ↓ 🟠 УРОВЕНЬ 3: ВАРИАНТЫ ТОВАРОВ (оранжевый - orange-500) ``` #### Цветовое кодирование: - Каждый уровень имеет цветной индикатор увеличивающегося размера - Цветная левая граница с увеличивающимся отступом и толщиной - Скроллбары в цвете уровня - Контрастный цвет текста для читаемости ## 3. 📊 ФУНКЦИОНАЛЬНЫЕ ВОЗМОЖНОСТИ > 📖 **Бизнес-правила**: См. [rules-complete.md#11-кабинет-фулфилмента](./rules-complete.md#11--кабинет-фулфилмента) для правил workflow и процессов ### 3.1 Структура склада по модулям (ОБЯЗАТЕЛЬНАЯ ПОСЛЕДОВАТЕЛЬНОСТЬ) 1. **📦 ПРОДУКТЫ** - готовые к отправке товары 2. **🛒 ТОВАРЫ** - базовые товары от поставщиков - **"На складе"** - готовы к обработке - **"В обработке"** - в процессе создания продукта 3. **❌ БРАК** - товары с дефектами, требуют утилизации 4. **↩️ ВОЗВРАТЫ С ПВЗ** - возвращенные товары, к обработке 5. **🎯 РАСХОДНИКИ СЕЛЛЕРОВ** - материалы для селлеров 6. **⚙️ РАСХОДНИКИ ФУЛФИЛМЕНТА** - операционные материалы (КЛИКАБЕЛЬНЫЙ модуль) ### 3.2 Система учета склада #### Показатели движения (дополнительные значения): - **ПРИБЫЛО** - количество поступивших на склад за период - **УБЫЛО** - количество списанных со склада за период #### Текущие остатки (основные значения): - **ФОРМУЛА**: Основные значения = Предыдущие остатки + Прибыло - Убыло - **ОБНОВЛЕНИЕ**: В реальном времени с изменениями за сутки - **ИСТОЧНИК**: GraphQL query `GET_FULFILLMENT_WAREHOUSE_STATS` ### 3.3 Поставки фулфилмента (`/fulfillment-supplies`) #### Структура: 2 основные вкладки **A) 🛒 ПОСТАВКИ ТОВАРОВ**: - **Детализированные товары ФФ** - планы и факты поставок с маршрутами - **Товары ФФ** - общие поставки товаров от селлеров - **Возвраты с ПВЗ** - обработка возвращенных товаров **B) 🔧 ПОСТАВКИ РАСХОДНИКОВ**: - **Заказы расходников** - управление заказами от селлеров - **Расходники селлеров** - материалы для клиентов - **Создание поставок** - формирование новых поставок расходников #### Workflow поставок товаров: 1. **Planned** - поставка запланирована 2. **In-transit** - товар в пути 3. **Delivered** - доставлен на склад 4. **Completed** - обработка завершена ### 3.4 Услуги фулфилмента (`/services`) #### Архитектура интеграции с системой **СВЯЗЬ С РЕЦЕПТУРАМИ СЕЛЛЕРОВ:** ``` СЕЛЛЕР (создание поставки) └── Рецептура ├── Товар (от поставщика) ├── Услуги фулфилмента ← CRUD в разделе Услуги ├── Расходники селлера └── Расходники фулфилмента ← ТОЛЬКО с установленной ценой ↓ ФУЛФИЛМЕНТ (обработка) ├── Входящие поставки → Поставки расходников (создание) └── Услуги → Расходники (установка цены за единицу) ``` #### Структура: 3 обязательные вкладки **A) 🛠️ УСЛУГИ** (`defaultValue="services"`): - **Полный CRUD**: создание, редактирование, удаление услуг - **Поля**: `name`, `description`, `price`, `imageUrl` - **Glass Upload Zone**: элегантная загрузка изображений - **Назначение**: каталог услуг для рецептур селлеров **B) 🚚 ЛОГИСТИКА**: - **Полный CRUD**: маршруты доставки - **Поля**: откуда → куда, тарификация до/свыше 1м³ - **Группированные локации**: - Мой фулфилмент (название организации) - Рынки (предустановленные) - Склады Wildberries - Склады Ozon - **Glass Upload Zone**: для изображений маршрутов **C) 📦 РАСХОДНИКИ** (**❌ БЕЗ СОЗДАНИЯ**): - **ТОЛЬКО ПРОСМОТР** расходников с фулфилмент-склада - **ЕДИНСТВЕННОЕ РЕДАКТИРУЕМОЕ ПОЛЕ**: `pricePerUnit` - цена за единицу для рецептур - **UI подсветка**: "Цена за 1 {unit}" (например, "Цена за 1 шт") - **Автоматическая синхронизация** при приеме поставки расходников - **Glass Upload Zone**: для обновления изображений расходников #### Workflow расходников: **ШАГ 1 - СОЗДАНИЕ**: Только через "Входящие поставки → Поставки расходников фулфилмента" **ШАГ 2 - СИНХРОНИЗАЦИЯ**: При приеме на склад → автоматически в Услуги/Расходники **ШАГ 3 - ЦЕНООБРАЗОВАНИЕ**: Установка цены за единицу в разделе Услуги **ШАГ 4 - ИСПОЛЬЗОВАНИЕ**: Доступны в рецептурах селлеров #### Правила видимости в рецептурах: **В РЕЦЕПТУРАХ СЕЛЛЕРОВ ПОКАЗЫВАЮТСЯ ТОЛЬКО:** - `isAvailable = true` (есть на складе) - `pricePerUnit != null` (цена установлена) **НЕ ПОКАЗЫВАЮТСЯ:** - Расходники без цены (`pricePerUnit = null`) - Удаленные со склада (`isAvailable = false`) ### 3.5 Разделение цен закупки и продажи **КРИТИЧЕСКОЕ ПРАВИЛО**: Расходники фулфилмента имеют **ДВЕ РАЗНЫЕ ЦЕНЫ**: 1. **ЦЕНА ЗАКУПКИ** (`Supply.price`) - цена покупки у поставщика 2. **ЦЕНА ПРОДАЖИ** (`Supply.pricePerUnit`) - цена продажи селлерам #### Поля в базе данных: ```prisma model Supply { price Decimal @db.Decimal(10, 2) // Цена закупки у поставщика (НЕИЗМЕННАЯ) pricePerUnit Decimal? @db.Decimal(10, 2) // Цена продажи селлерам (устанавливается фулфилментом) } ``` #### Правила отображения по разделам: **РАЗДЕЛ "СКЛАД → РАСХОДНИКИ ФУЛФИЛМЕНТА"**: - Показывает `Supply.price` (цена закупки) - Цена ТОЛЬКО ДЛЯ ЧТЕНИЯ, нельзя изменять - Отражает историческую стоимость приобретения **РАЗДЕЛ "УСЛУГИ → РАСХОДНИКИ"**: - Показывает и редактирует `Supply.pricePerUnit` (цена продажи) - Единственное место где можно изменить цену для селлеров - Влияет на рецептуры и расчеты для селлеров #### Бизнес-логика создания: ```typescript // ПРИ ПОСТУПЛЕНИИ ОТ ПОСТАВЩИКА: const supply = await prisma.supply.create({ data: { price: item.price, // Цена поставщика → ЗАФИКСИРОВАНА pricePerUnit: null, // Цена продажи → ПУСТАЯ }, }) // УСТАНОВКА ЦЕНЫ ПРОДАЖИ (в разделе "Услуги"): const updated = await prisma.supply.update({ data: { pricePerUnit: newPrice, // ТОЛЬКО цена продажи // price НЕ ТРОГАЕМ - остается цена закупки }, }) ``` ### 3.6 Сотрудники фулфилмента (`/employees`) #### Структура: 2 основные вкладки **A) 👥 СОТРУДНИКИ** (`defaultValue="combined"`): **Управление персоналом**: - **CRUD операции**: создание, редактирование, удаление сотрудников - **Статусы сотрудников**: `ACTIVE`, `VACATION`, `SICK`, `FIRED` - **Формы добавления**: Компактная (`showCompactForm`) / Полная форма - **Поиск и фильтрация** по имени, должности, статусу **Табель рабочего времени**: - **Навигация по месяцам**: текущий год/месяц с кнопками ←/→ - **Отметки по дням**: статус дня и количество отработанных часов **B) 📋 ОТЧЕТЫ** (`value="reports"`): - **Сводные отчеты** по сотрудникам за период - **Экспорт данных** табеля - **Аналитика рабочего времени** ### 3.7 Статистика фулфилмента (`/fulfillment-statistics`) #### Блоки аналитики (сворачиваемые): **1. НАКОПЛЕННАЯ СТАТИСТИКА** (`allTime: true`): - Обработано товаров (общий объем) - Выявлено брака (всего единиц) - Поставок получено - Общий доход (за все время) - Выполнено заказов (успешных отгрузок) - Удовлетворенность клиентов (средний рейтинг) **2. ОТГРУЗКА НА ПЛОЩАДКИ** (`marketplaces: true`): - Отправлено на Wildberries - Отправлено на Ozon - Отправлено на другие площадки **3. АНАЛИТИКА ПРОИЗВОДИТЕЛЬНОСТИ** (`performance: false`): - Среднее время обработки (на единицу товара) - Уровень брака (от общего объема) - Уровень возвратов (возвраты с площадок) - Удовлетворенность клиентов ## 4. 🛠️ GRAPHQL API ### 4.1 Основные запросы (Queries) #### Получение статистики склада: ```graphql query GetFulfillmentWarehouseStats { fulfillmentWarehouseStats { products { total received shipped } supplies { onStock inProcessing } defects { total pending } returns { pending processed } } } ``` #### Получение услуг фулфилмента: ```graphql query GetMyServices { myServices { id name description price imageUrl isActive } } ``` #### Получение расходников с ценами: ```graphql query GetMySupplies { mySupplies { id name price # Цена закупки (read-only) pricePerUnit # Цена продажи (editable) unit isAvailable warehouseConsumableId } } ``` #### Получение логистических маршрутов: ```graphql query GetMyLogistics { myLogistics { id fromLocation toLocation tariffUnder1m3 tariffOver1m3 description } } ``` #### Получение сотрудников: ```graphql query GetEmployees { employees { id name position status schedule { date hoursWorked status } } } ``` ### 4.2 Мутации (Mutations) #### Создание услуги: ```graphql mutation CreateService($input: ServiceInput!) { createService(input: $input) { success service { id name description price organization { id name fullName } } } } ``` #### Обновление цены расходника: ```graphql mutation UpdateSupplyPrice($supplyId: ID!, $pricePerUnit: Float!) { updateSupplyPrice(id: $supplyId, pricePerUnit: $pricePerUnit) { success supply { id pricePerUnit isAvailable } } } ``` #### Создание логистического маршрута: ```graphql mutation CreateLogistics($input: LogisticsInput!) { createLogistics(input: $input) { success logistics { id fromLocation toLocation tariffUnder1m3 tariffOver1m3 organization { id name fullName } } } } ``` #### Управление сотрудниками: ```graphql mutation CreateEmployee($input: EmployeeInput!) { createEmployee(input: $input) { success employee { id name position status } } } mutation UpdateEmployeeSchedule($employeeId: ID!, $date: String!, $hoursWorked: Int!, $status: String!) { updateEmployeeSchedule(employeeId: $employeeId, date: $date, hoursWorked: $hoursWorked, status: $status) { success schedule { date hoursWorked status } } } ``` ### 4.3 Специальные запросы для рецептур #### Расходники доступные для рецептур (только с ценой): ```graphql query GetAvailableSuppliesForRecipe { availableSuppliesForRecipe { id name pricePerUnit # Только те что != null unit imageUrl } } ``` #### GraphQL типы: ```graphql type Supply { id: ID! name: String! price: Float! # Цена закупки (неизменная) pricePerUnit: Float # Цена продажи (может быть null) unit: String! # "шт", "кг", "м" isAvailable: Boolean! # Статус на складе warehouseConsumableId: ID! # Связь со складом imageUrl: String } type Service { id: ID! name: String! description: String price: Float! imageUrl: String organization: Organization! } type Logistics { id: ID! fromLocation: String! toLocation: String! tariffUnder1m3: Float! tariffOver1m3: Float! description: String organization: Organization! } ``` ## 5. 📁 ТЕХНИЧЕСКИЕ КОМПОНЕНТЫ ### 5.1 Расположение компонентов ``` src/components/ ├── fulfillment/ # Компоненты фулфилмента │ ├── fulfillment-warehouse-dashboard.tsx │ ├── fulfillment-supplies-dashboard.tsx │ ├── fulfillment-statistics.tsx │ └── warehouse-module-card.tsx ├── services/ # Компоненты услуг │ ├── services-management.tsx │ ├── service-card.tsx │ ├── logistics-management.tsx │ └── consumables-pricing.tsx ├── employees/ # Компоненты сотрудников │ ├── employee-management.tsx │ ├── employee-card.tsx │ ├── employee-schedule.tsx │ └── employee-reports.tsx └── economics/ # Экономика └── fulfillment-economics-page.tsx ``` ### 5.2 Страницы (Pages) ``` src/app/ ├── warehouse/ │ └── page.tsx # Склад фулфилмента ├── fulfillment-supplies/ │ └── page.tsx # Поставки фулфилмента ├── services/ │ └── page.tsx # Услуги и расходники ├── employees/ │ └── page.tsx # Сотрудники └── fulfillment-statistics/ └── page.tsx # Статистика ``` ## 6. 🚨 ТЕХНИЧЕСКИЕ ПРАВИЛА И ОГРАНИЧЕНИЯ > 📖 **Workflow процессов**: См. [rules-complete.md#11-кабинет-фулфилмента](./rules-complete.md#11--кабинет-фулфилмента) для бизнес-процессов ### 6.1 Обязательные проверки: - Проверка типа организации: `organization.type === 'FULFILLMENT'` - Валидация прав доступа на уровне GraphQL резолверов - Проверка наличия товаров на складе перед отгрузкой - Контроль цен расходников перед отображением в рецептурах ### 6.2 Правила безопасности доступа: #### Контроль на уровне компонентов: ```typescript {user?.organization?.type === "FULFILLMENT" && ( )} // Для полинга данных const { data } = useQuery(GET_FULFILLMENT_STATS, { skip: user?.organization?.type !== 'FULFILLMENT', pollInterval: 60000 }) ``` #### Проверки в GraphQL резолверах: ```typescript // Проверка что пользователь - фулфилмент if (context.user.organization.type !== 'FULFILLMENT') { throw new Error('Access denied: Fulfillment access required') } // Проверка доступа к своим услугам const service = await prisma.service.findFirst({ where: { id: serviceId, organizationId: context.user.organizationId, }, }) // Обязательное включение organization в ответы const updated = await prisma.service.update({ where: { id: serviceId }, data: input, include: { organization: true }, // ОБЯЗАТЕЛЬНО! }) ``` ### 6.3 Запрещено: - Создавать расходники в разделе "Услуги" (только через поставки) - Изменять цену закупки (`Supply.price`) - она фиксируется при поступлении - Показывать в рецептурах расходники без установленной цены продажи - Удалять услуги, используемые в активных рецептурах ### 6.4 Правила создания и ценообразования: - **Услуги**: Создаются фулфилментом с установленной ценой - **Расходники**: Создаются только при поступлении от поставщиков - **Цена продажи**: Устанавливается отдельно в разделе Услуги - **Логистика**: Тарификация до/свыше 1м³ обязательна ### 6.5 Правила фулфилмента **ОБЯЗАТЕЛЬНО**: - Установка цен на расходники перед доступностью селлерам - Контроль качества товаров при приемке - Своевременная обработка возвратов - Ведение учета движения товаров по модулям - Управление персоналом и рабочим временем **ЗАПРЕЩЕНО**: - Отгружать товары без подтверждения наличия - Создавать расходники минуя систему поставок - Изменять цены закупки после поступления товара - Показывать в рецептурах неактивные расходники - Нарушать последовательность модулей склада **ИНТЕГРАЦИЯ С ПАРТНЕРАМИ**: - Фулфилмент видит всех партнеров системы - Принимает поставки от всех типов организаций - Предоставляет услуги селлерам через рецептуры - Все взаимодействия фиксируются в системе уведомлений ### 6.6 АВТОМАТИЧЕСКИЕ ЗАПИСИ В ТАБЛИЦЕ СКЛАДА ПРИ НОВОМ ПАРТНЕРСТВЕ #### **ПРАВИЛО АВТОСОЗДАНИЯ ЗАПИСЕЙ В СКЛАДЕ** **ТРИГГЕР**: При создании нового партнерства с селлером (`SELLER`) автоматически создается запись в таблице склада фулфилмента. **УСЛОВИЕ СРАБАТЫВАНИЯ**: - Новая организация типа `SELLER` становится партнером фулфилмента - Создание происходит через любой механизм партнерства: - Партнерские ссылки (`?partner=REFERRAL_CODE`) - Коммерческие взаимодействия - Прямое добавление в контрагенты #### **АВТОМАТИЧЕСКИ СОЗДАВАЕМЫЕ ДАННЫЕ** **Структура записи в таблице склада**: ```typescript // StoreData - верхний уровень (СИНИЙ УРОВЕНЬ) interface AutoCreatedStoreEntry { id: string // Генерируется автоматически storeName: string // Название организации селлера storeOwner: string // ИНН или название селлера storeImage?: string // Логотип организации (если есть) storeQuantity: number // 0 (пока нет поставок) products: ProductItem[] // Пустой массив изначально } ``` #### **ЗНАЧЕНИЯ ПО УМОЛЧАНИЮ**: - **storeName**: `organization.fullName` или `organization.name` - **storeOwner**: `organization.inn` или `organization.fullName` - **storeImage**: `organization.logoUrl` (если заполнено) - **storeQuantity**: `0` (нет поставок) - **products**: `[]` (пустой массив) - **Все вложенные количества**: `0` #### **БИЗНЕС-ЛОГИКА ОБНОВЛЕНИЯ** **ПРИ ПЕРВОЙ ПОСТАВКЕ ОТ СЕЛЛЕРА**: ```typescript // Автоматически обновляются значения: storeEntry.storeQuantity = totalProductsReceived storeEntry.products = [ { id: generatedId, productName: supply.productName, productQuantity: supply.quantity, productPlace: supply.warehouseLocation || 'A1-1', variants: [] // Заполняется при обработке вариантов } ] ``` **ОТОБРАЖЕНИЕ В ТАБЛИЦЕ СКЛАДА**: - Новые партнеры отображаются сразу после создания партнерства - Показывают нулевые значения до первых поставок - Цветовое кодирование: СИНИЙ уровень (store level) - Размещаются в самом верху таблицы #### **ТЕХНИЧЕСКАЯ РЕАЛИЗАЦИЯ** **GraphQL мутация (автоматический вызов)**: ```graphql mutation AutoCreateWarehouseEntry($partnerId: ID!) { autoCreateWarehouseEntry(partnerId: $partnerId) { success warehouseEntry { id storeName storeOwner storeImage storeQuantity } } } ``` **Триггер на создание партнерства**: ```typescript // При создании нового партнера-селлера const createPartnership = async (sellerId: string, fulfillmentId: string) => { // 1. Создаем партнерство const partnership = await createPartnership(sellerId, fulfillmentId) // 2. Автоматически создаем запись в складе if (partnership.success) { await autoCreateWarehouseEntry(sellerId) } } ``` #### **ПРАВИЛА ОТОБРАЖЕНИЯ** **В ТАБЛИЦЕ СКЛАДА ФУЛФИЛМЕНТА**: - ✅ Показывать всех партнеров-селлеров (даже с нулевыми поставками) - ✅ Новые партнеры размещаются в самом верху таблицы - ✅ Стандартное цветовое кодирование для всех партнеров **ЦВЕТОВОЕ КОДИРОВАНИЕ**: - **Синий уровень** (партнеры): стандартное отображение для всех - **Обычный белый цвет текста**: для всех партнеров независимо от статуса поставок **КООРДИНАТНАЯ СИСТЕМА**: - Новым партнерам резервируется место: `Quantity: 0 | Location: -` - При первой поставке координаты назначаются: `A1-1`, `A1-2`, и т.д. #### **ОБЯЗАТЕЛЬНЫЕ ПОЛЯ ДЛЯ ПАРТНЕРОВ** **МИНИМАЛЬНЫЕ ТРЕБОВАНИЯ**: ```prisma model Organization { id String @id @default(cuid()) name String // ОБЯЗАТЕЛЬНО для storeName fullName String? // Приоритет для storeName inn String? // Для storeOwner logoUrl String? // Для storeImage type OrganizationType // SELLER } ``` **ВАЛИДАЦИЯ ПРИ СОЗДАНИИ ПАРТНЕРСТВА**: - Проверка что организация типа `SELLER` - Проверка что не существует дубликатов в складе - Генерация уникального ID для записи склада #### **ИНТЕГРАЦИЯ С СУЩЕСТВУЮЩИМИ КОМПОНЕНТАМИ** **В компоненте таблицы склада**: ```typescript // Сортировка: новые партнеры в верху таблицы const sortStores = (stores: StoreData[]) => { return stores.sort((a, b) => { // Новые партнеры (quantity = 0) в самом верху if (a.storeQuantity === 0 && b.storeQuantity > 0) return -1 if (a.storeQuantity > 0 && b.storeQuantity === 0) return 1 // Остальная сортировка по количеству или дате return b.storeQuantity - a.storeQuantity }) } ``` **В GraphQL запросах склада**: ```graphql query GetWarehouseData { warehouseData { stores { id storeName storeOwner storeImage storeQuantity partnershipDate # Для сортировки новых партнеров products { # Существующая структура } } } } ``` > 📖 **Критические запреты**: См. [rules-complete.md#17-критические-запреты](./rules-complete.md#17--критические-запреты) --- **Последнее обновление**: Август 2025 **Связанные файлы**: - [rules-complete.md](./rules-complete.md) - Общие бизнес-правила - [visual-design-rules.md](./visual-design-rules.md) - Визуальные правила