feat: Add real-time warehouse statistics with daily changes for fulfillment centers

- Added new GraphQL query `GET_FULFILLMENT_WAREHOUSE_STATS` to fetch warehouse statistics with daily changes
- Created comprehensive GraphQL resolver `fulfillmentWarehouseStats` that calculates:
  * Current quantities for products, goods, defects, pvzReturns, fulfillmentSupplies, sellerSupplies
  * Daily changes (absolute numbers) based on deliveries in the last 24 hours
  * Percentage changes for all categories
- Updated fulfillment warehouse dashboard to use real GraphQL data instead of static calculations
- Added polling every 60 seconds to keep statistics up-to-date
- Enhanced StatCard component to display accurate percentage and absolute changes
- Statistics now show real supply deliveries and changes relative to the previous day

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Bivekich
2025-07-31 14:46:00 +03:00
parent f8bb8508cb
commit 76a40e0eed
4 changed files with 266 additions and 85 deletions

View File

@ -17,6 +17,7 @@ import {
GET_WAREHOUSE_PRODUCTS,
GET_MY_SUPPLIES, // Расходники селлеров
GET_MY_FULFILLMENT_SUPPLIES, // Расходники фулфилмента
GET_FULFILLMENT_WAREHOUSE_STATS, // Статистика склада с изменениями за сутки
} from "@/graphql/queries";
import { toast } from "sonner";
import {
@ -219,6 +220,17 @@ export function FulfillmentWarehouseDashboard() {
fetchPolicy: "cache-and-network",
});
// Загружаем статистику склада с изменениями за сутки
const {
data: warehouseStatsData,
loading: warehouseStatsLoading,
error: warehouseStatsError,
refetch: refetchWarehouseStats,
} = useQuery(GET_FULFILLMENT_WAREHOUSE_STATS, {
fetchPolicy: "cache-and-network",
pollInterval: 60000, // Обновляем каждую минуту
});
// Получаем данные магазинов, заказов и товаров
const allCounterparties = counterpartiesData?.myCounterparties || [];
const sellerPartners = allCounterparties.filter(
@ -383,107 +395,49 @@ export function FulfillmentWarehouseDashboard() {
netChange: suppliesReceivedToday - suppliesUsedToday,
});
// Подсчитываем статистику на основе реальных данных из заказов поставок
// Получаем статистику склада из GraphQL (с реальными изменениями за сутки)
const warehouseStats: WarehouseStats = useMemo(() => {
const inTransitOrders = supplyOrders.filter(
(o) => o.status === "IN_TRANSIT"
);
const deliveredOrders = supplyOrders.filter(
(o) => o.status === "DELIVERED"
);
// Подсчитываем общее количество товаров из всех доставленных заказов
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
);
// Подсчитываем расходники фулфилмента из нового резолвера
// Основное значение = текущий остаток на складе
const totalFulfillmentSupplies = myFulfillmentSupplies.reduce(
(sum: number, supply: any) => sum + (supply.currentStock || 0),
0
);
// Дополнительные значения - динамика за сегодня
const today = new Date();
today.setHours(0, 0, 0, 0);
// Поставлено сегодня (дополнительное значение +)
const fulfillmentSuppliesReceivedToday = myFulfillmentSupplies
.filter((supply: any) => {
const supplyDate = new Date(supply.updatedAt || supply.createdAt);
supplyDate.setHours(0, 0, 0, 0);
return (
supplyDate.getTime() === today.getTime() &&
supply.status === "available"
);
})
.reduce(
(sum: number, supply: any) => sum + (supply.quantity || 0), // Поставленное количество
0
);
// Использовано сегодня (дополнительное значение -)
const fulfillmentSuppliesUsedToday = myFulfillmentSupplies
.filter((supply: any) => {
const supplyDate = new Date(supply.updatedAt || supply.createdAt);
supplyDate.setHours(0, 0, 0, 0);
return supplyDate.getTime() === today.getTime();
})
.reduce(
(sum: number, supply: any) => sum + (supply.usedStock || 0), // Использованное количество
0
);
// Итоговое изменение = поставлено - использовано
const fulfillmentSuppliesChange =
fulfillmentSuppliesReceivedToday - fulfillmentSuppliesUsedToday;
// Если данные еще загружаются, возвращаем нули
if (warehouseStatsLoading || !warehouseStatsData?.fulfillmentWarehouseStats) {
return {
products: { current: 0, change: 0 },
goods: { current: 0, change: 0 },
defects: { current: 0, change: 0 },
pvzReturns: { current: 0, change: 0 },
fulfillmentSupplies: { current: 0, change: 0 },
sellerSupplies: { current: 0, change: 0 },
};
}
// Используем данные из GraphQL резолвера
const stats = warehouseStatsData.fulfillmentWarehouseStats;
return {
products: {
current: 0, // Нет данных о готовых продуктах для продажи
change: 0, // Нет данных об изменениях продуктов
current: stats.products.current,
change: stats.products.change,
},
goods: {
current: 0, // Нет реальных данных о готовых товарах
change: 0, // Нет реальных данных об изменениях готовых товаров
current: stats.goods.current,
change: stats.goods.change,
},
defects: {
current: 0, // Нет реальных данных о браке
change: 0, // Нет реальных данных об изменениях брака
current: stats.defects.current,
change: stats.defects.change,
},
pvzReturns: {
current: 0, // Нет реальных данных о возвратах с ПВЗ
change: 0, // Нет реальных данных об изменениях возвратов
current: stats.pvzReturns.current,
change: stats.pvzReturns.change,
},
fulfillmentSupplies: {
current: totalFulfillmentSupplies, // Основное значение: текущий остаток на складе
change: fulfillmentSuppliesChange, // Дополнительное значение: поставлено - использовано за сегодня
current: stats.fulfillmentSupplies.current,
change: stats.fulfillmentSupplies.change,
},
sellerSupplies: {
current: totalSellerSupplies, // Реальное количество расходников селлера из базы
change: suppliesReceivedToday - suppliesUsedToday, // Реальное изменение за сутки
current: stats.sellerSupplies.current,
change: stats.sellerSupplies.change,
},
};
}, [
sellerPartners,
supplyOrders,
allProducts,
mySupplies,
myFulfillmentSupplies,
suppliesReceivedToday,
suppliesUsedToday,
productsReceivedToday,
productsUsedToday,
user?.organization?.id,
]);
}, [warehouseStatsData, warehouseStatsLoading]);
// Создаем структурированные данные склада на основе уникальных товаров
const storeData: StoreData[] = useMemo(() => {