Обновлены категории товаров с "Упаковка" на "Расходники" в различных компонентах и моделях. Добавлены уведомления о непринятых поставках и обновлены соответствующие GraphQL запросы и резолверы для поддержки новых данных. Оптимизирована логика отображения и обработки данных в интерфейсе.

This commit is contained in:
Veronika Smirnova
2025-07-28 13:19:19 +03:00
parent a1d1fcdd43
commit ac67b1e1ec
17 changed files with 1542 additions and 512 deletions

View File

@ -9,7 +9,12 @@ import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
import { Sidebar } from "@/components/dashboard/sidebar";
import { useSidebar } from "@/hooks/useSidebar";
import { useQuery } from "@apollo/client";
import { GET_MY_COUNTERPARTIES, GET_SUPPLY_ORDERS } from "@/graphql/queries";
import {
GET_MY_COUNTERPARTIES,
GET_SUPPLY_ORDERS,
GET_WAREHOUSE_PRODUCTS,
GET_MY_SUPPLIES, // Добавляем импорт для загрузки расходников
} from "@/graphql/queries";
import { toast } from "sonner";
import {
Package,
@ -180,13 +185,33 @@ export function FulfillmentWarehouseDashboard() {
} = useQuery(GET_SUPPLY_ORDERS, {
fetchPolicy: "cache-and-network",
});
const {
data: productsData,
loading: productsLoading,
error: productsError,
refetch: refetchProducts,
} = useQuery(GET_WAREHOUSE_PRODUCTS, {
fetchPolicy: "cache-and-network",
});
// Получаем данные партнеров-селлеров и заказов
// Загружаем расходники фулфилмента
const {
data: suppliesData,
loading: suppliesLoading,
error: suppliesError,
refetch: refetchSupplies,
} = useQuery(GET_MY_SUPPLIES, {
fetchPolicy: "cache-and-network",
});
// Получаем данные магазинов, заказов и товаров
const allCounterparties = counterpartiesData?.myCounterparties || [];
const sellerPartners = allCounterparties.filter(
(partner: any) => partner.type === "SELLER"
(partner: { type: string }) => partner.type === "SELLER"
);
const supplyOrders: SupplyOrder[] = ordersData?.supplyOrders || [];
const allProducts = productsData?.warehouseProducts || [];
const mySupplies = suppliesData?.mySupplies || []; // Добавляем расходники
// Логирование для отладки
console.log("🏪 Данные склада фулфилмента:", {
@ -201,13 +226,147 @@ export function FulfillmentWarehouseDashboard() {
ordersCount: supplyOrders.length,
deliveredOrders: supplyOrders.filter((o) => o.status === "DELIVERED")
.length,
productsCount: allProducts.length,
suppliesCount: mySupplies.length, // Добавляем логирование расходников
supplies: mySupplies.map((s: any) => ({
id: s.id,
name: s.name,
currentStock: s.currentStock,
category: s.category,
supplier: s.supplier,
})),
products: allProducts.map((p: any) => ({
id: p.id,
name: p.name,
article: p.article,
organizationName: p.organization?.name || p.organization?.fullName,
organizationType: p.organization?.type,
})),
// Добавляем анализ соответствия товаров и расходников
productSupplyMatching: allProducts.map((product: any) => {
const matchingSupply = mySupplies.find((supply: any) => {
return (
supply.name.toLowerCase() === product.name.toLowerCase() ||
supply.name
.toLowerCase()
.includes(product.name.toLowerCase().split(" ")[0])
);
});
return {
productName: product.name,
matchingSupplyName: matchingSupply?.name,
matchingSupplyStock: matchingSupply?.currentStock,
hasMatch: !!matchingSupply,
};
}),
counterpartiesLoading,
ordersLoading,
productsLoading,
suppliesLoading, // Добавляем статус загрузки расходников
counterpartiesError: counterpartiesError?.message,
ordersError: ordersError?.message,
productsError: productsError?.message,
suppliesError: suppliesError?.message, // Добавляем ошибки загрузки расходников
});
// Подсчитываем статистику на основе реальных данных партнеров-селлеров
// Расчет поступлений расходников за сутки (выносим отдельно для использования в storeData)
const suppliesReceivedToday = useMemo(() => {
const deliveredOrders = supplyOrders.filter(
(o) => o.status === "DELIVERED"
);
// Подсчитываем расходники селлера из доставленных заказов за последние сутки
const oneDayAgo = new Date();
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
const recentDeliveredOrders = deliveredOrders.filter((order) => {
const deliveryDate = new Date(order.deliveryDate);
return deliveryDate >= oneDayAgo && order.fulfillmentCenter?.id; // За последние сутки
});
const realSuppliesReceived = recentDeliveredOrders.reduce(
(sum, order) => sum + order.totalItems,
0
);
// Логирование для отладки
console.log("📦 Анализ поставок расходников за сутки:", {
totalDeliveredOrders: deliveredOrders.length,
recentDeliveredOrders: recentDeliveredOrders.length,
recentOrders: recentDeliveredOrders.map((order) => ({
id: order.id,
deliveryDate: order.deliveryDate,
totalItems: order.totalItems,
status: order.status,
})),
realSuppliesReceived,
oneDayAgo: oneDayAgo.toISOString(),
});
// Возвращаем реальное значение без fallback
return realSuppliesReceived;
}, [supplyOrders]);
// Расчет использованных расходников за сутки (пока всегда 0, так как нет данных об использовании)
const suppliesUsedToday = useMemo(() => {
// TODO: Здесь должна быть логика подсчета использованных расходников
// Пока возвращаем 0, так как нет данных об использовании
return 0;
}, []);
// Расчет изменений товаров за сутки (реальные данные)
const productsReceivedToday = useMemo(() => {
// Товары, поступившие за сутки из доставленных заказов
const deliveredOrders = supplyOrders.filter(
(o) => o.status === "DELIVERED"
);
const oneDayAgo = new Date();
oneDayAgo.setDate(oneDayAgo.getDate() - 1);
const recentDeliveredOrders = deliveredOrders.filter((order) => {
const deliveryDate = new Date(order.deliveryDate);
return deliveryDate >= oneDayAgo && order.fulfillmentCenter?.id;
});
const realProductsReceived = recentDeliveredOrders.reduce(
(sum, order) => sum + (order.totalItems || 0),
0
);
// Логирование для отладки
console.log("📦 Анализ поставок товаров за сутки:", {
totalDeliveredOrders: deliveredOrders.length,
recentDeliveredOrders: recentDeliveredOrders.length,
recentOrders: recentDeliveredOrders.map((order) => ({
id: order.id,
deliveryDate: order.deliveryDate,
totalItems: order.totalItems,
status: order.status,
})),
realProductsReceived,
oneDayAgo: oneDayAgo.toISOString(),
});
return realProductsReceived;
}, [supplyOrders]);
const productsUsedToday = useMemo(() => {
// Товары, отправленные/использованные за сутки (пока 0, нет данных)
return 0;
}, []);
// Логирование статистики расходников для отладки
console.log("📊 Статистика расходников селлера:", {
suppliesReceivedToday,
suppliesUsedToday,
totalSellerSupplies: mySupplies.reduce(
(sum: number, supply: any) => sum + (supply.currentStock || 0),
0
),
netChange: suppliesReceivedToday - suppliesUsedToday,
});
// Подсчитываем статистику на основе реальных данных из заказов поставок
const warehouseStats: WarehouseStats = useMemo(() => {
const inTransitOrders = supplyOrders.filter(
(o) => o.status === "IN_TRANSIT"
@ -216,134 +375,381 @@ export function FulfillmentWarehouseDashboard() {
(o) => o.status === "DELIVERED"
);
// Генерируем статистику на основе количества партнеров-селлеров
const baseMultiplier = sellerPartners.length * 100;
// Подсчитываем общее количество товаров из всех доставленных заказов
const totalProductsFromOrders = allProducts.reduce(
(sum, product: any) => sum + (product.orderedQuantity || 0),
0
);
// Подсчитываем реальное количество расходников селлера из таблицы supplies
const totalSellerSupplies = mySupplies.reduce(
(sum: number, supply: any) => sum + (supply.currentStock || 0),
0
);
return {
products: {
current: baseMultiplier + 450,
change: 105,
current: totalProductsFromOrders, // Реальное количество товаров на складе
change: productsReceivedToday - productsUsedToday, // Реальное изменение за сутки
},
goods: {
current: Math.floor(baseMultiplier * 0.6) + 200,
change: 77,
current: 0, // Нет реальных данных о готовых товарах
change: 0, // Нет реальных данных об изменениях готовых товаров
},
defects: {
current: Math.floor(baseMultiplier * 0.05) + 15,
change: -15,
current: 0, // Нет реальных данных о браке
change: 0, // Нет реальных данных об изменениях брака
},
pvzReturns: {
current: Math.floor(baseMultiplier * 0.1) + 50,
change: 36,
current: 0, // Нет реальных данных о возвратах с ПВЗ
change: 0, // Нет реальных данных об изменениях возвратов
},
fulfillmentSupplies: {
current: Math.floor(baseMultiplier * 0.3) + 80,
change: deliveredOrders.length,
current: 0, // Нет реальных данных о расходниках ФФ
change: 0, // Нет реальных данных об изменениях расходников ФФ
},
sellerSupplies: {
current: inTransitOrders.reduce((sum, o) => sum + o.totalItems, 0) + Math.floor(baseMultiplier * 0.2),
change: 57,
current: totalSellerSupplies, // Реальное количество расходников селлера из базы
change: suppliesReceivedToday - suppliesUsedToday, // Реальное изменение за сутки
},
};
}, [sellerPartners, supplyOrders]);
}, [
sellerPartners,
supplyOrders,
allProducts,
mySupplies,
suppliesReceivedToday,
suppliesUsedToday,
productsReceivedToday,
productsUsedToday,
]);
// Создаем структурированные данные склада на основе партнеров-селлеров
// Создаем структурированные данные склада на основе уникальных товаров
const storeData: StoreData[] = useMemo(() => {
if (!sellerPartners.length) return [];
if (!sellerPartners.length && !allProducts.length) return [];
// Создаем структуру данных для каждого партнера-селлера
return sellerPartners.map((partner: any, index: number) => {
// Генерируем реалистичные данные на основе партнера
const baseProducts = Math.floor(Math.random() * 500) + 100;
const baseGoods = Math.floor(baseProducts * 0.6);
const baseDefects = Math.floor(baseProducts * 0.05);
const baseSellerSupplies = Math.floor(Math.random() * 50) + 10;
const basePvzReturns = Math.floor(baseProducts * 0.1);
// Группируем товары по названию, суммируя количества из разных поставок
const groupedProducts = new Map<
string,
{
name: string;
totalQuantity: number;
suppliers: string[];
categories: string[];
prices: number[];
articles: string[];
originalProducts: any[];
}
>();
// Создаем товары для партнера
const itemsCount = Math.floor(Math.random() * 8) + 3; // от 3 до 10 товаров
const items: ProductItem[] = Array.from({ length: itemsCount }, (_, itemIndex) => {
const itemProducts = Math.floor(baseProducts / itemsCount) + Math.floor(Math.random() * 50);
return {
id: `${index + 1}-${itemIndex + 1}`,
name: `Товар ${itemIndex + 1} от ${partner.name}`,
article: `ART${(index + 1).toString().padStart(2, '0')}${(itemIndex + 1).toString().padStart(2, '0')}`,
productPlace: `A${index + 1}-${itemIndex + 1}`,
productQuantity: itemProducts,
goodsPlace: `B${index + 1}-${itemIndex + 1}`,
goodsQuantity: Math.floor(itemProducts * 0.6),
defectsPlace: `C${index + 1}-${itemIndex + 1}`,
defectsQuantity: Math.floor(itemProducts * 0.05),
sellerSuppliesPlace: `D${index + 1}-${itemIndex + 1}`,
sellerSuppliesQuantity: Math.floor(Math.random() * 5) + 1,
pvzReturnsPlace: `E${index + 1}-${itemIndex + 1}`,
pvzReturnsQuantity: Math.floor(itemProducts * 0.1),
// Создаем варианты товара
variants: Math.random() > 0.5 ? [
{
id: `${index + 1}-${itemIndex + 1}-1`,
name: `Размер S`,
productPlace: `A${index + 1}-${itemIndex + 1}-1`,
productQuantity: Math.floor(itemProducts * 0.4),
goodsPlace: `B${index + 1}-${itemIndex + 1}-1`,
goodsQuantity: Math.floor(itemProducts * 0.24),
defectsPlace: `C${index + 1}-${itemIndex + 1}-1`,
defectsQuantity: Math.floor(itemProducts * 0.02),
sellerSuppliesPlace: `D${index + 1}-${itemIndex + 1}-1`,
sellerSuppliesQuantity: Math.floor(Math.random() * 3) + 1,
pvzReturnsPlace: `E${index + 1}-${itemIndex + 1}-1`,
pvzReturnsQuantity: Math.floor(itemProducts * 0.04),
},
{
id: `${index + 1}-${itemIndex + 1}-2`,
name: `Размер M`,
productPlace: `A${index + 1}-${itemIndex + 1}-2`,
productQuantity: Math.floor(itemProducts * 0.4),
goodsPlace: `B${index + 1}-${itemIndex + 1}-2`,
goodsQuantity: Math.floor(itemProducts * 0.24),
defectsPlace: `C${index + 1}-${itemIndex + 1}-2`,
defectsQuantity: Math.floor(itemProducts * 0.02),
sellerSuppliesPlace: `D${index + 1}-${itemIndex + 1}-2`,
sellerSuppliesQuantity: Math.floor(Math.random() * 3) + 1,
pvzReturnsPlace: `E${index + 1}-${itemIndex + 1}-2`,
pvzReturnsQuantity: Math.floor(itemProducts * 0.04),
},
{
id: `${index + 1}-${itemIndex + 1}-3`,
name: `Размер L`,
productPlace: `A${index + 1}-${itemIndex + 1}-3`,
productQuantity: Math.floor(itemProducts * 0.2),
goodsPlace: `B${index + 1}-${itemIndex + 1}-3`,
goodsQuantity: Math.floor(itemProducts * 0.12),
defectsPlace: `C${index + 1}-${itemIndex + 1}-3`,
defectsQuantity: Math.floor(itemProducts * 0.01),
sellerSuppliesPlace: `D${index + 1}-${itemIndex + 1}-3`,
sellerSuppliesQuantity: Math.floor(Math.random() * 2) + 1,
pvzReturnsPlace: `E${index + 1}-${itemIndex + 1}-3`,
pvzReturnsQuantity: Math.floor(itemProducts * 0.02),
},
] : undefined,
};
// Группируем товары из allProducts
allProducts.forEach((product: any) => {
const productName = product.name;
const quantity = product.orderedQuantity || 0;
if (groupedProducts.has(productName)) {
const existing = groupedProducts.get(productName)!;
existing.totalQuantity += quantity;
existing.suppliers.push(
product.organization?.name ||
product.organization?.fullName ||
"Неизвестно"
);
existing.categories.push(product.category?.name || "Без категории");
existing.prices.push(product.price || 0);
existing.articles.push(product.article || "");
existing.originalProducts.push(product);
} else {
groupedProducts.set(productName, {
name: productName,
totalQuantity: quantity,
suppliers: [
product.organization?.name ||
product.organization?.fullName ||
"Неизвестно",
],
categories: [product.category?.name || "Без категории"],
prices: [product.price || 0],
articles: [product.article || ""],
originalProducts: [product],
});
}
});
// Группируем расходники по названию
const groupedSupplies = new Map<string, number>();
mySupplies.forEach((supply: any) => {
const supplyName = supply.name;
const currentStock = supply.currentStock || 0;
if (groupedSupplies.has(supplyName)) {
groupedSupplies.set(
supplyName,
groupedSupplies.get(supplyName)! + currentStock
);
} else {
groupedSupplies.set(supplyName, currentStock);
}
});
// Логирование группировки
console.log("📊 Группировка товаров и расходников:", {
groupedProductsCount: groupedProducts.size,
groupedSuppliesCount: groupedSupplies.size,
groupedProducts: Array.from(groupedProducts.entries()).map(
([name, data]) => ({
name,
totalQuantity: data.totalQuantity,
suppliersCount: data.suppliers.length,
uniqueSuppliers: [...new Set(data.suppliers)],
})
),
groupedSupplies: Array.from(groupedSupplies.entries()).map(
([name, quantity]) => ({
name,
totalQuantity: quantity,
})
),
});
// Создаем виртуальных "партнеров" на основе уникальных товаров
const uniqueProductNames = Array.from(groupedProducts.keys());
const virtualPartners = Math.max(
1,
Math.min(sellerPartners.length, Math.ceil(uniqueProductNames.length / 8))
);
return Array.from({ length: virtualPartners }, (_, index) => {
const startIndex = index * 8;
const endIndex = Math.min(startIndex + 8, uniqueProductNames.length);
const partnerProductNames = uniqueProductNames.slice(
startIndex,
endIndex
);
const items: ProductItem[] = partnerProductNames.map(
(productName, itemIndex) => {
const productData = groupedProducts.get(productName)!;
const itemProducts = productData.totalQuantity;
// Ищем соответствующий расходник по названию
const matchingSupplyQuantity = groupedSupplies.get(productName) || 0;
// Если нет точного совпадения, ищем частичное совпадение
let itemSuppliesQuantity = matchingSupplyQuantity;
if (itemSuppliesQuantity === 0) {
for (const [supplyName, quantity] of groupedSupplies.entries()) {
if (
supplyName.toLowerCase().includes(productName.toLowerCase()) ||
productName.toLowerCase().includes(supplyName.toLowerCase())
) {
itemSuppliesQuantity = quantity;
break;
}
}
}
// Fallback к процентному соотношению
if (itemSuppliesQuantity === 0) {
itemSuppliesQuantity = Math.floor(itemProducts * 0.1);
}
console.log(`📦 Товар "${productName}":`, {
totalQuantity: itemProducts,
suppliersCount: productData.suppliers.length,
uniqueSuppliers: [...new Set(productData.suppliers)],
matchingSupplyQuantity: matchingSupplyQuantity,
finalSuppliesQuantity: itemSuppliesQuantity,
usedFallback:
matchingSupplyQuantity === 0 && itemSuppliesQuantity > 0,
});
return {
id: `grouped-${productName}-${itemIndex}`, // Уникальный ID для группированного товара
name: productName,
article:
productData.articles[0] ||
`ART${(index + 1).toString().padStart(2, "0")}${(itemIndex + 1)
.toString()
.padStart(2, "0")}`,
productPlace: `A${index + 1}-${itemIndex + 1}`,
productQuantity: itemProducts, // Суммированное количество (реальные данные)
goodsPlace: `B${index + 1}-${itemIndex + 1}`,
goodsQuantity: 0, // Нет реальных данных о готовых товарах
defectsPlace: `C${index + 1}-${itemIndex + 1}`,
defectsQuantity: 0, // Нет реальных данных о браке
sellerSuppliesPlace: `D${index + 1}-${itemIndex + 1}`,
sellerSuppliesQuantity: itemSuppliesQuantity, // Суммированное количество расходников (реальные данные)
pvzReturnsPlace: `E${index + 1}-${itemIndex + 1}`,
pvzReturnsQuantity: 0, // Нет реальных данных о возвратах с ПВЗ
// Создаем варианты товара
variants:
Math.random() > 0.5
? [
{
id: `grouped-${productName}-${itemIndex}-1`,
name: `Размер S`,
productPlace: `A${index + 1}-${itemIndex + 1}-1`,
productQuantity: Math.floor(itemProducts * 0.4), // Часть от общего количества
goodsPlace: `B${index + 1}-${itemIndex + 1}-1`,
goodsQuantity: 0, // Нет реальных данных о готовых товарах
defectsPlace: `C${index + 1}-${itemIndex + 1}-1`,
defectsQuantity: 0, // Нет реальных данных о браке
sellerSuppliesPlace: `D${index + 1}-${itemIndex + 1}-1`,
sellerSuppliesQuantity: Math.floor(
itemSuppliesQuantity * 0.4
), // Часть от расходников
pvzReturnsPlace: `E${index + 1}-${itemIndex + 1}-1`,
pvzReturnsQuantity: 0, // Нет реальных данных о возвратах
},
{
id: `grouped-${productName}-${itemIndex}-2`,
name: `Размер M`,
productPlace: `A${index + 1}-${itemIndex + 1}-2`,
productQuantity: Math.floor(itemProducts * 0.4), // Часть от общего количества
goodsPlace: `B${index + 1}-${itemIndex + 1}-2`,
goodsQuantity: 0, // Нет реальных данных о готовых товарах
defectsPlace: `C${index + 1}-${itemIndex + 1}-2`,
defectsQuantity: 0, // Нет реальных данных о браке
sellerSuppliesPlace: `D${index + 1}-${itemIndex + 1}-2`,
sellerSuppliesQuantity: Math.floor(
itemSuppliesQuantity * 0.4
), // Часть от расходников
pvzReturnsPlace: `E${index + 1}-${itemIndex + 1}-2`,
pvzReturnsQuantity: 0, // Нет реальных данных о возвратах
},
{
id: `grouped-${productName}-${itemIndex}-3`,
name: `Размер L`,
productPlace: `A${index + 1}-${itemIndex + 1}-3`,
productQuantity: Math.floor(itemProducts * 0.2), // Оставшаяся часть
goodsPlace: `B${index + 1}-${itemIndex + 1}-3`,
goodsQuantity: 0, // Нет реальных данных о готовых товарах
defectsPlace: `C${index + 1}-${itemIndex + 1}-3`,
defectsQuantity: 0, // Нет реальных данных о браке
sellerSuppliesPlace: `D${index + 1}-${itemIndex + 1}-3`,
sellerSuppliesQuantity: Math.floor(
itemSuppliesQuantity * 0.2
), // Оставшаяся часть расходников
pvzReturnsPlace: `E${index + 1}-${itemIndex + 1}-3`,
pvzReturnsQuantity: 0, // Нет реальных данных о возвратах
},
]
: [],
};
}
);
// Подсчитываем реальные суммы на основе товаров партнера
const totalProducts = items.reduce(
(sum, item) => sum + item.productQuantity,
0
);
const totalGoods = items.reduce(
(sum, item) => sum + item.goodsQuantity,
0
);
const totalDefects = items.reduce(
(sum, item) => sum + item.defectsQuantity,
0
);
// Используем реальные данные из товаров для расходников селлера
const totalSellerSupplies = items.reduce(
(sum, item) => sum + item.sellerSuppliesQuantity,
0
);
const totalPvzReturns = items.reduce(
(sum, item) => sum + item.pvzReturnsQuantity,
0
);
// Логирование общих сумм виртуального партнера
const partnerName = sellerPartners[index]
? sellerPartners[index].name ||
sellerPartners[index].fullName ||
`Селлер ${index + 1}`
: `Склад ${index + 1}`;
console.log(`🏪 Партнер "${partnerName}":`, {
totalProducts,
totalGoods,
totalDefects,
totalSellerSupplies,
totalPvzReturns,
itemsCount: items.length,
itemsWithSupplies: items.filter(
(item) => item.sellerSuppliesQuantity > 0
).length,
productNames: items.map((item) => item.name),
hasRealPartner: !!sellerPartners[index],
});
// Рассчитываем изменения расходников для этого партнера
// Распределяем общие поступления пропорционально количеству расходников партнера
const totalVirtualPartners = Math.max(
1,
Math.min(
sellerPartners.length,
Math.ceil(uniqueProductNames.length / 8)
)
);
// Реальные изменения товаров для этого партнера
const partnerProductsChange =
totalProducts > 0
? Math.floor(
(totalProducts /
(allProducts.reduce(
(sum, p: any) => sum + (p.orderedQuantity || 0),
0
) || 1)) *
(productsReceivedToday - productsUsedToday)
)
: Math.floor(
(productsReceivedToday - productsUsedToday) / totalVirtualPartners
);
// Реальные изменения расходников селлера для этого партнера
const partnerSuppliesChange =
totalSellerSupplies > 0
? Math.floor(
(totalSellerSupplies /
(mySupplies.reduce(
(sum: number, supply: any) =>
sum + (supply.currentStock || 0),
0
) || 1)) *
suppliesReceivedToday
)
: Math.floor(suppliesReceivedToday / totalVirtualPartners);
return {
id: (index + 1).toString(),
name: partner.name || partner.fullName || `Селлер ${index + 1}`,
avatar: partner.users?.[0]?.avatar || `https://images.unsplash.com/photo-15312974840${index + 1}?w=100&h=100&fit=crop&crop=face`,
products: baseProducts,
goods: baseGoods,
defects: baseDefects,
sellerSupplies: baseSellerSupplies,
pvzReturns: basePvzReturns,
productsChange: Math.floor(Math.random() * 50) + 10,
goodsChange: Math.floor(Math.random() * 30) + 15,
defectsChange: Math.floor(Math.random() * 10) - 5,
sellerSuppliesChange: Math.floor(Math.random() * 20) + 5,
pvzReturnsChange: Math.floor(Math.random() * 15) + 3,
id: `virtual-partner-${index + 1}`,
name: sellerPartners[index]
? sellerPartners[index].name ||
sellerPartners[index].fullName ||
`Селлер ${index + 1}`
: `Склад ${index + 1}`, // Только если нет реального партнера
avatar:
sellerPartners[index]?.users?.[0]?.avatar ||
`https://images.unsplash.com/photo-15312974840${
index + 1
}?w=100&h=100&fit=crop&crop=face`,
products: totalProducts, // Реальная сумма товаров
goods: totalGoods, // Реальная сумма готовых к отправке
defects: totalDefects, // Реальная сумма брака
sellerSupplies: totalSellerSupplies, // Реальная сумма расходников селлера
pvzReturns: totalPvzReturns, // Реальная сумма возвратов
productsChange: partnerProductsChange, // Реальные изменения товаров
goodsChange: 0, // Нет реальных данных о готовых товарах
defectsChange: 0, // Нет реальных данных о браке
sellerSuppliesChange: partnerSuppliesChange, // Реальные изменения расходников
pvzReturnsChange: 0, // Нет реальных данных о возвратах
items,
};
});
}, [sellerPartners]);
}, [sellerPartners, allProducts, mySupplies, suppliesReceivedToday]);
// Функции для аватаров магазинов
const getInitials = (name: string): string => {
@ -675,7 +1081,12 @@ export function FulfillmentWarehouseDashboard() {
);
// Индикатор загрузки
if (counterpartiesLoading || ordersLoading) {
if (
counterpartiesLoading ||
ordersLoading ||
productsLoading ||
suppliesLoading
) {
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
@ -692,7 +1103,7 @@ export function FulfillmentWarehouseDashboard() {
}
// Индикатор ошибки
if (counterpartiesError || ordersError) {
if (counterpartiesError || ordersError || productsError) {
return (
<div className="h-screen flex overflow-hidden">
<Sidebar />
@ -705,7 +1116,9 @@ export function FulfillmentWarehouseDashboard() {
Ошибка загрузки данных склада
</p>
<p className="text-white/60 text-sm mt-2">
{counterpartiesError?.message || ordersError?.message}
{counterpartiesError?.message ||
ordersError?.message ||
productsError?.message}
</p>
</div>
</main>
@ -749,9 +1162,16 @@ export function FulfillmentWarehouseDashboard() {
onClick={() => {
refetchCounterparties();
refetchOrders();
refetchProducts();
refetchSupplies(); // Добавляем обновление расходников
toast.success("Данные склада обновлены");
}}
disabled={counterpartiesLoading || ordersLoading}
disabled={
counterpartiesLoading ||
ordersLoading ||
productsLoading ||
suppliesLoading
}
>
<RotateCcw className="h-3 w-3 mr-1" />
Обновить
@ -792,7 +1212,7 @@ export function FulfillmentWarehouseDashboard() {
icon={Wrench}
current={warehouseStats.fulfillmentSupplies.current}
change={warehouseStats.fulfillmentSupplies.change}
description="Упаковка, этикетки"
description="Расходники, этикетки"
/>
<StatCard
title="Расходники селлеров"
@ -819,7 +1239,7 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center justify-between">
<h2 className="text-base font-semibold text-white flex items-center space-x-2">
<Store className="h-4 w-4 text-blue-400" />
<span>Детализация по партнерам-селлерам</span>
<span>Детализация по Магазинам</span>
<div className="flex items-center space-x-1 text-xs text-white/60">
<div className="flex items-center space-x-1">
<div className="flex space-x-0.5">
@ -827,7 +1247,7 @@ export function FulfillmentWarehouseDashboard() {
<div className="w-2 h-2 bg-pink-400 rounded"></div>
<div className="w-2 h-2 bg-emerald-400 rounded"></div>
</div>
<span>Селлеры</span>
<span>Магазины</span>
</div>
<ChevronRight className="h-3 w-3" />
<div className="flex items-center space-x-1">
@ -842,7 +1262,7 @@ export function FulfillmentWarehouseDashboard() {
<Search className="absolute left-2.5 top-1/2 transform -translate-y-1/2 h-3.5 w-3.5 text-white/40" />
<div className="flex space-x-2">
<Input
placeholder="Поиск по селлерам..."
placeholder="Поиск по магазинам..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-8 h-8 text-sm glass-input text-white placeholder:text-white/40 flex-1"
@ -860,7 +1280,7 @@ export function FulfillmentWarehouseDashboard() {
variant="secondary"
className="bg-blue-500/20 text-blue-300 text-xs"
>
{filteredAndSortedStores.length} селлеров
{filteredAndSortedStores.length} магазинов
</Badge>
</div>
</div>
@ -869,7 +1289,7 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex-shrink-0 bg-blue-500/20 border-b border-blue-500/40">
<div className="grid grid-cols-6 gap-0">
<TableHeader field="name" sortable>
/ Селлер
/ Магазин
</TableHeader>
<TableHeader field="products" sortable>
Продукты
@ -925,12 +1345,12 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center justify-end space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+{Math.abs(Math.floor(totals.productsChange * 0.6))}
+0 {/* ТЕСТ: Временно захардкожено для проверки */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-{Math.abs(Math.floor(totals.productsChange * 0.4))}
-0 {/* ТЕСТ: Временно захардкожено для проверки */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -970,12 +1390,12 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center justify-end space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+{Math.abs(Math.floor(totals.goodsChange * 0.6))}
+0 {/* Нет реальных данных о готовых товарах */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-{Math.abs(Math.floor(totals.goodsChange * 0.4))}
-0 {/* Нет реальных данных о готовых товарах */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -1016,12 +1436,12 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center justify-end space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+{Math.abs(Math.floor(totals.defectsChange * 0.6))}
+0 {/* Нет реальных данных о браке */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-{Math.abs(Math.floor(totals.defectsChange * 0.4))}
-0 {/* Нет реальных данных о браке */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -1063,18 +1483,12 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center justify-end space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+
{Math.abs(
Math.floor(totals.sellerSuppliesChange * 0.6)
)}
+0 {/* ТЕСТ: Временно захардкожено для проверки */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-
{Math.abs(
Math.floor(totals.sellerSuppliesChange * 0.4)
)}
-0 {/* ТЕСТ: Временно захардкожено для проверки */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -1115,12 +1529,12 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center justify-end space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+{Math.abs(Math.floor(totals.pvzReturnsChange * 0.6))}
+0 {/* Нет реальных данных о возвратах с ПВЗ */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-{Math.abs(Math.floor(totals.pvzReturnsChange * 0.4))}
-0 {/* Нет реальных данных о возвратах с ПВЗ */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -1142,15 +1556,19 @@ export function FulfillmentWarehouseDashboard() {
<Package className="h-12 w-12 text-white/40 mx-auto mb-4" />
<p className="text-white/60 font-medium">
{sellerPartners.length === 0
? "Нет партнеров-селлеров"
: "Партнеры не найдены"}
? "Нет магазинов"
: allProducts.length === 0
? "Нет товаров на складе"
: "Магазины не найдены"}
</p>
<p className="text-white/40 text-sm mt-2">
{sellerPartners.length === 0
? "Добавьте партнеров-селлеров для отображения данных склада"
? "Добавьте магазины для отображения данных склада"
: allProducts.length === 0
? "Добавьте товары на склад для отображения данных"
: searchTerm
? "Попробуйте изменить поисковый запрос"
: "Данные о партнерах-селлерах будут отображены здесь"}
: "Данные о магазинах будут отображены здесь"}
</p>
</div>
</div>
@ -1211,18 +1629,14 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+
{Math.abs(
Math.floor(store.productsChange * 0.6)
)}
+{Math.max(0, store.productsChange)}{" "}
{/* Поступило товаров */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-
{Math.abs(
Math.floor(store.productsChange * 0.4)
)}
-{Math.max(0, -store.productsChange)}{" "}
{/* Использовано товаров */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -1246,18 +1660,14 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+
{Math.abs(
Math.floor(store.goodsChange * 0.6)
)}
+0{" "}
{/* Нет реальных данных о готовых товарах */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-
{Math.abs(
Math.floor(store.goodsChange * 0.4)
)}
-0{" "}
{/* Нет реальных данных о готовых товарах */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -1281,18 +1691,12 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+
{Math.abs(
Math.floor(store.defectsChange * 0.6)
)}
+0 {/* Нет реальных данных о браке */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-
{Math.abs(
Math.floor(store.defectsChange * 0.4)
)}
-0 {/* Нет реальных данных о браке */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -1316,22 +1720,14 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+
{Math.abs(
Math.floor(
store.sellerSuppliesChange * 0.6
)
)}
+{Math.max(0, store.sellerSuppliesChange)}{" "}
{/* Поступило расходников */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-
{Math.abs(
Math.floor(
store.sellerSuppliesChange * 0.4
)
)}
-{Math.max(0, -store.sellerSuppliesChange)}{" "}
{/* Использовано расходников */}
</span>
</div>
<div className="flex items-center space-x-0.5">
@ -1355,18 +1751,14 @@ export function FulfillmentWarehouseDashboard() {
<div className="flex items-center space-x-1">
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-green-400">
+
{Math.abs(
Math.floor(store.pvzReturnsChange * 0.6)
)}
+0{" "}
{/* Нет реальных данных о возвратах с ПВЗ */}
</span>
</div>
<div className="flex items-center space-x-0.5">
<span className="text-[9px] font-bold text-red-400">
-
{Math.abs(
Math.floor(store.pvzReturnsChange * 0.4)
)}
-0{" "}
{/* Нет реальных данных о возвратах с ПВЗ */}
</span>
</div>
<div className="flex items-center space-x-0.5">