Создан единый источник истины rules-complete.md v9.1 с полной интеграцией всех правил системы. Консолидированы правила создания предметов по ролям, уточнен статус брака (НЕ РЕАЛИЗОВАНО), обновлен механизм учета ПЛАН/ФАКТ с заменой брака на потери при пересчете. Добавлен экономический учет расходников фулфилмента для селлера через рецептуру. Удалены дублирующие файлы правил (CLAUDE.md, development-checklist.md, work-protocols.md, violation-prevention-protocol.md, self-validation.md, description.md). Интегрированы UI структуры создания поставок и концепция многоуровневых таблиц.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-05 15:29:41 +03:00
parent ee72a9488b
commit d30e3f9666
23 changed files with 2038 additions and 6162 deletions

View File

@ -5,6 +5,8 @@ import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { StatsCard } from "../ui/stats-card";
import { StatsGrid } from "../ui/stats-grid";
import { useQuery } from "@apollo/client";
import { GET_MY_WILDBERRIES_SUPPLIES } from "@/graphql/queries";
import {
Calendar,
Package,
@ -66,129 +68,31 @@ interface WbSupply {
status: "planned" | "in-transit" | "delivered" | "completed";
}
// Моковые данные для поставок на Wildberries
const mockWbSupplies: WbSupply[] = [
{
id: "wb1",
number: 4001,
supplyId: "WB24010001",
deliveryDate: "2024-01-22",
createdDate: "2024-01-16",
status: "delivered",
plannedTotal: 120,
actualTotal: 118,
defectTotal: 2,
totalProductPrice: 2400000,
totalLogisticsPrice: 18000,
grandTotal: 2418000,
routes: [
{
id: "wbr1",
from: "Садовод",
fromAddress: "Москва, 14-й км МКАД",
to: "WB Подольск",
toAddress: "Подольск, ул. Складская, 25",
totalProductPrice: 2400000,
logisticsPrice: 18000,
totalAmount: 2418000,
warehouses: [
{
id: "wbw1",
name: "Склад WB Подольск",
address: "Подольск, ул. Складская, 25",
warehouseId: 117501,
totalAmount: 2400000,
products: [
{
id: "wbp1",
name: "Смартфон Samsung Galaxy S24",
sku: "SAMS-GS24-256",
nmId: 123456789,
category: "Смартфоны и гаджеты",
plannedQty: 40,
actualQty: 39,
defectQty: 1,
productPrice: 65000,
},
{
id: "wbp2",
name: "Чехол для Samsung Galaxy S24",
sku: "CASE-GS24-BLK",
nmId: 987654321,
category: "Аксессуары для телефонов",
plannedQty: 80,
actualQty: 79,
defectQty: 1,
productPrice: 1200,
},
],
},
],
},
],
},
{
id: "wb2",
number: 4002,
supplyId: "WB24010002",
deliveryDate: "2024-01-28",
createdDate: "2024-01-20",
status: "in-transit",
plannedTotal: 60,
actualTotal: 60,
defectTotal: 0,
totalProductPrice: 1800000,
totalLogisticsPrice: 15000,
grandTotal: 1815000,
routes: [
{
id: "wbr2",
from: "ТЯК Москва",
fromAddress: "Москва, Алтуфьевское шоссе, 27",
to: "WB Электросталь",
toAddress: "Электросталь, ул. Промышленная, 10",
totalProductPrice: 1800000,
logisticsPrice: 15000,
totalAmount: 1815000,
warehouses: [
{
id: "wbw2",
name: "Склад WB Электросталь",
address: "Электросталь, ул. Промышленная, 10",
warehouseId: 117986,
totalAmount: 1800000,
products: [
{
id: "wbp3",
name: "Наушники Sony WH-1000XM5",
sku: "SONY-WH1000XM5",
nmId: 555666777,
category: "Наушники и аудио",
plannedQty: 30,
actualQty: 30,
defectQty: 0,
productPrice: 35000,
},
{
id: "wbp4",
name: "Кабель USB-C",
sku: "CABLE-USBC-2M",
nmId: 111222333,
category: "Кабели и адаптеры",
plannedQty: 30,
actualQty: 30,
defectQty: 0,
productPrice: 800,
},
],
},
],
},
],
},
];
export function WildberriesSuppliesTab() {
// Загружаем реальные данные поставок на Wildberries
const { data: wbSuppliesData, loading, error } = useQuery(GET_MY_WILDBERRIES_SUPPLIES, {
errorPolicy: 'all'
});
// Преобразуем данные из GraphQL в нужный формат
const wbSupplies: WbSupply[] = (wbSuppliesData?.myWildberriesSupplies || [])
.map((supply: any, index: number) => ({
id: supply.id,
number: index + 4000, // Начинаем с 4000 для WB поставок
supplyId: `WB${new Date().getFullYear()}${String(index + 1).padStart(6, '0')}`,
deliveryDate: supply.deliveryDate || new Date().toISOString().split('T')[0],
createdDate: supply.createdAt?.split('T')[0] || new Date().toISOString().split('T')[0],
status: supply.status === 'DELIVERED' ? 'delivered' : 'in-transit',
plannedTotal: supply.totalItems || 0,
actualTotal: supply.totalItems || 0,
defectTotal: 0,
totalProductPrice: supply.totalAmount || 0,
totalLogisticsPrice: 0,
grandTotal: supply.totalAmount || 0,
routes: []
}));
const [expandedSupplies, setExpandedSupplies] = useState<Set<string>>(
new Set()
);
@ -316,7 +220,7 @@ export function WildberriesSuppliesTab() {
<StatsGrid>
<StatsCard
title="Поставок на WB"
value={mockWbSupplies.length}
value={loading ? 0 : wbSupplies.length}
icon={ShoppingBag}
iconColor="text-purple-400"
iconBg="bg-purple-500/20"
@ -327,7 +231,7 @@ export function WildberriesSuppliesTab() {
<StatsCard
title="Сумма WB поставок"
value={formatCurrency(
mockWbSupplies.reduce((sum, supply) => sum + supply.grandTotal, 0)
loading ? 0 : wbSupplies.reduce((sum, supply) => sum + supply.grandTotal, 0)
)}
icon={TrendingUp}
iconColor="text-green-400"
@ -339,7 +243,7 @@ export function WildberriesSuppliesTab() {
<StatsCard
title="В пути"
value={
mockWbSupplies.filter((supply) => supply.status === "in-transit")
loading ? 0 : wbSupplies.filter((supply) => supply.status === "in-transit")
.length
}
icon={Calendar}
@ -351,7 +255,7 @@ export function WildberriesSuppliesTab() {
<StatsCard
title="С браком"
value={
mockWbSupplies.filter((supply) => supply.defectTotal > 0).length
loading ? 0 : wbSupplies.filter((supply) => supply.defectTotal > 0).length
}
icon={AlertTriangle}
iconColor="text-red-400"
@ -395,7 +299,25 @@ export function WildberriesSuppliesTab() {
</tr>
</thead>
<tbody>
{mockWbSupplies.map((supply) => {
{loading && (
<tr>
<td colSpan={11} className="p-8 text-center">
<div className="text-white/60">Загрузка данных...</div>
</td>
</tr>
)}
{!loading && wbSupplies.length === 0 && (
<tr>
<td colSpan={11} className="p-8 text-center">
<div className="text-white/60">
<ShoppingBag className="h-12 w-12 mx-auto mb-4 text-white/20" />
<div className="text-lg font-semibold text-white mb-2">Поставки на Wildberries не найдены</div>
<div>Создайте первую поставку товаров на Wildberries</div>
</div>
</td>
</tr>
)}
{!loading && wbSupplies.map((supply) => {
const isSupplyExpanded = expandedSupplies.has(supply.id);
return (