From 593ae16e1e8347ad47a1fad0f15e01133f60817e Mon Sep 17 00:00:00 2001 From: Bivekich Date: Wed, 30 Jul 2025 18:32:52 +0300 Subject: [PATCH] a lot of --- ...ставки расходников фулфилмента.md => logic_full.md | 0 prisma/schema.prisma | 3 + ...te-fulfillment-consumables-supply-page.tsx | 44 ++++++ .../delivery-details.tsx | 20 ++- .../fulfillment-supplies-page.tsx | 26 +++- .../fulfillment-warehouse-dashboard.tsx | 1 + .../fulfillment-warehouse/supplies-grid.tsx | 2 - .../fulfillment-warehouse/supply-card.tsx | 8 -- src/components/fulfillment-warehouse/types.ts | 1 - src/graphql/resolvers.ts | 135 ++++++++++++++---- 10 files changed, 192 insertions(+), 48 deletions(-) rename логика поставки расходников фулфилмента.md => logic_full.md (100%) diff --git a/логика поставки расходников фулфилмента.md b/logic_full.md similarity index 100% rename from логика поставки расходников фулфилмента.md rename to logic_full.md diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8007563..fd5cbf1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -106,6 +106,7 @@ model Organization { supplyOrders SupplyOrder[] partnerSupplyOrders SupplyOrder[] @relation("SupplyOrderPartner") fulfillmentSupplyOrders SupplyOrder[] @relation("SupplyOrderFulfillmentCenter") + logisticsSupplyOrders SupplyOrder[] @relation("SupplyOrderLogistics") wildberriesSupplies WildberriesSupply[] supplySuppliers SupplySupplier[] @relation("SupplySuppliers") externalAds ExternalAd[] @relation("ExternalAds") @@ -472,6 +473,7 @@ model SupplyOrder { totalAmount Decimal @db.Decimal(12, 2) totalItems Int fulfillmentCenterId String? + logisticsPartnerId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt organizationId String @@ -479,6 +481,7 @@ model SupplyOrder { organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) partner Organization @relation("SupplyOrderPartner", fields: [partnerId], references: [id]) fulfillmentCenter Organization? @relation("SupplyOrderFulfillmentCenter", fields: [fulfillmentCenterId], references: [id]) + logisticsPartner Organization? @relation("SupplyOrderLogistics", fields: [logisticsPartnerId], references: [id]) @@map("supply_orders") } diff --git a/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx index f89415e..1220126 100644 --- a/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx +++ b/src/components/fulfillment-supplies/create-fulfillment-consumables-supply-page.tsx @@ -83,6 +83,8 @@ export function CreateFulfillmentConsumablesSupplyPage() { const { user } = useAuth(); const [selectedSupplier, setSelectedSupplier] = useState(null); + const [selectedLogistics, setSelectedLogistics] = + useState(null); const [selectedConsumables, setSelectedConsumables] = useState< SelectedFulfillmentConsumable[] >([]); @@ -113,6 +115,11 @@ export function CreateFulfillmentConsumablesSupplyPage() { counterpartiesData?.myCounterparties || [] ).filter((org: FulfillmentConsumableSupplier) => org.type === "WHOLESALE"); + // Фильтруем только логистические компании + const logisticsPartners = ( + counterpartiesData?.myCounterparties || [] + ).filter((org: FulfillmentConsumableSupplier) => org.type === "LOGIST"); + // Фильтруем поставщиков по поисковому запросу const filteredSuppliers = consumableSuppliers.filter( (supplier: FulfillmentConsumableSupplier) => @@ -258,6 +265,7 @@ export function CreateFulfillmentConsumablesSupplyPage() { deliveryDate: deliveryDate, // Для фулфилмента указываем себя как получателя (поставка на свой склад) fulfillmentCenterId: user?.organization?.id, + logisticsPartnerId: selectedLogistics?.id, items: selectedConsumables.map((consumable) => ({ productId: consumable.id, quantity: consumable.selectedQuantity, @@ -784,6 +792,42 @@ export function CreateFulfillmentConsumablesSupplyPage() { required /> + + {/* Выбор логистики */} +
+ +
+ +
+ + + +
+
+
Итого: diff --git a/src/components/fulfillment-warehouse/delivery-details.tsx b/src/components/fulfillment-warehouse/delivery-details.tsx index f36048d..3a71f58 100644 --- a/src/components/fulfillment-warehouse/delivery-details.tsx +++ b/src/components/fulfillment-warehouse/delivery-details.tsx @@ -16,8 +16,8 @@ import { import { DeliveryDetailsProps } from "./types"; const DELIVERY_STATUS_CONFIG = { - delivered: { - label: "Доставлено", + "in-stock": { + label: "На складе", color: "bg-green-500/20 text-green-300", icon: CheckCircle, }, @@ -26,6 +26,22 @@ const DELIVERY_STATUS_CONFIG = { color: "bg-blue-500/20 text-blue-300", icon: Truck, }, + confirmed: { + label: "Подтверждено", + color: "bg-cyan-500/20 text-cyan-300", + icon: CheckCircle, + }, + planned: { + label: "Запланировано", + color: "bg-yellow-500/20 text-yellow-300", + icon: Clock, + }, + // Обратная совместимость + delivered: { + label: "Доставлено", + color: "bg-green-500/20 text-green-300", + icon: CheckCircle, + }, pending: { label: "Ожидание", color: "bg-yellow-500/20 text-yellow-300", diff --git a/src/components/fulfillment-warehouse/fulfillment-supplies-page.tsx b/src/components/fulfillment-warehouse/fulfillment-supplies-page.tsx index cc98c54..fd6bbde 100644 --- a/src/components/fulfillment-warehouse/fulfillment-supplies-page.tsx +++ b/src/components/fulfillment-warehouse/fulfillment-supplies-page.tsx @@ -32,6 +32,27 @@ import { // Статусы расходников с цветами const STATUS_CONFIG = { + "in-stock": { + label: "Доступен", + color: "bg-green-500/20 text-green-300", + icon: CheckCircle, + }, + "in-transit": { + label: "В пути", + color: "bg-blue-500/20 text-blue-300", + icon: Clock, + }, + confirmed: { + label: "Подтверждено", + color: "bg-cyan-500/20 text-cyan-300", + icon: CheckCircle, + }, + planned: { + label: "Запланировано", + color: "bg-yellow-500/20 text-yellow-300", + icon: Clock, + }, + // Обратная совместимость и специальные статусы available: { label: "Доступен", color: "bg-green-500/20 text-green-300", @@ -47,11 +68,6 @@ const STATUS_CONFIG = { color: "bg-red-500/20 text-red-300", icon: AlertTriangle, }, - "in-transit": { - label: "В пути", - color: "bg-blue-500/20 text-blue-300", - icon: Clock, - }, reserved: { label: "Зарезервирован", color: "bg-purple-500/20 text-purple-300", diff --git a/src/components/fulfillment-warehouse/fulfillment-warehouse-dashboard.tsx b/src/components/fulfillment-warehouse/fulfillment-warehouse-dashboard.tsx index 19a9d9b..4272787 100644 --- a/src/components/fulfillment-warehouse/fulfillment-warehouse-dashboard.tsx +++ b/src/components/fulfillment-warehouse/fulfillment-warehouse-dashboard.tsx @@ -477,6 +477,7 @@ export function FulfillmentWarehouseDashboard() { supplyOrders, allProducts, mySupplies, + myFulfillmentSupplies, suppliesReceivedToday, suppliesUsedToday, productsReceivedToday, diff --git a/src/components/fulfillment-warehouse/supplies-grid.tsx b/src/components/fulfillment-warehouse/supplies-grid.tsx index a99222c..0527704 100644 --- a/src/components/fulfillment-warehouse/supplies-grid.tsx +++ b/src/components/fulfillment-warehouse/supplies-grid.tsx @@ -15,7 +15,6 @@ export function SuppliesGrid({ return (
{supplies.map((supply) => { - const statusConfig = getStatusConfig(supply.status); const isExpanded = expandedSupplies.has(supply.id); const deliveries = getSupplyDeliveries(supply); @@ -26,7 +25,6 @@ export function SuppliesGrid({ supply={supply} isExpanded={isExpanded} onToggleExpansion={onToggleExpansion} - statusConfig={statusConfig} getSupplyDeliveries={getSupplyDeliveries} /> diff --git a/src/components/fulfillment-warehouse/supply-card.tsx b/src/components/fulfillment-warehouse/supply-card.tsx index 56ee960..221ef28 100644 --- a/src/components/fulfillment-warehouse/supply-card.tsx +++ b/src/components/fulfillment-warehouse/supply-card.tsx @@ -18,7 +18,6 @@ export function SupplyCard({ supply, isExpanded, onToggleExpansion, - statusConfig, getSupplyDeliveries, }: SupplyCardProps) { const formatCurrency = (amount: number) => { @@ -33,7 +32,6 @@ export function SupplyCard({ return new Intl.NumberFormat("ru-RU").format(num); }; - const StatusIcon = statusConfig.icon; const isLowStock = supply.currentStock <= supply.minStock && supply.currentStock > 0; const stockPercentage = @@ -58,12 +56,6 @@ export function SupplyCard({ {supply.description}

-
- - - {statusConfig.label} - -
{/* Основная информация */} diff --git a/src/components/fulfillment-warehouse/types.ts b/src/components/fulfillment-warehouse/types.ts index 84199f4..c2cfe27 100644 --- a/src/components/fulfillment-warehouse/types.ts +++ b/src/components/fulfillment-warehouse/types.ts @@ -54,7 +54,6 @@ export interface SupplyCardProps { supply: Supply; isExpanded: boolean; onToggleExpansion: (id: string) => void; - statusConfig: StatusConfig; getSupplyDeliveries: (supply: Supply) => Supply[]; } diff --git a/src/graphql/resolvers.ts b/src/graphql/resolvers.ts index d6f53fa..3732e25 100644 --- a/src/graphql/resolvers.ts +++ b/src/graphql/resolvers.ts @@ -728,33 +728,57 @@ export const resolvers = { }, }); - // Получаем расходники селлеров из таблицы supply - // Это расходники, созданные при доставке заказов от селлеров - const existingSupplies = await prisma.supply.findMany({ + // Получаем ВСЕ расходники из таблицы supply для фулфилмента + const allSupplies = await prisma.supply.findMany({ where: { organizationId: currentUser.organization.id }, include: { organization: true }, orderBy: { createdAt: "desc" }, }); + // Получаем все заказы фулфилмента для себя (чтобы исключить их расходники) + const fulfillmentOwnOrders = await prisma.supplyOrder.findMany({ + where: { + organizationId: currentUser.organization.id, // Созданы фулфилментом + fulfillmentCenterId: currentUser.organization.id, // Для себя + status: "DELIVERED", + }, + include: { + items: { + include: { + product: true, + }, + }, + }, + }); + + // Создаем набор названий товаров из заказов фулфилмента для себя + const fulfillmentProductNames = new Set( + fulfillmentOwnOrders.flatMap((order) => + order.items.map((item) => item.product.name) + ) + ); + + // Фильтруем расходники: исключаем те, что созданы заказами фулфилмента для себя + const sellerSupplies = allSupplies.filter((supply) => { + // Если расходник соответствует товару из заказа фулфилмента для себя, + // то это расходник фулфилмента, а не селлера + return !fulfillmentProductNames.has(supply.name); + }); + // Логирование для отладки console.log("🔥🔥🔥 SELLER SUPPLIES RESOLVER CALLED 🔥🔥🔥"); console.log("📊 Расходники селлеров:", { organizationId: currentUser.organization.id, organizationType: currentUser.organization.type, - existingSuppliesCount: existingSupplies.length, + allSuppliesCount: allSupplies.length, + fulfillmentOwnOrdersCount: fulfillmentOwnOrders.length, + fulfillmentProductNames: Array.from(fulfillmentProductNames), + filteredSellerSuppliesCount: sellerSupplies.length, sellerOrdersCount: sellerSupplyOrders.length, - sellerOrders: sellerSupplyOrders.map((o) => ({ - id: o.id, - sellerName: o.organization.name, - supplierName: o.partner.name, - status: o.status, - itemsCount: o.items.length, - })), }); - // Возвращаем только расходники селлеров из таблицы supply - // TODO: В будущем можно добавить фильтрацию по источнику заказа - return existingSupplies; + // Возвращаем только расходники селлеров (исключая расходники фулфилмента) + return sellerSupplies; }, // Расходники фулфилмента (материалы для работы фулфилмента) @@ -818,12 +842,14 @@ export const resolvers = { category: item.product.category?.name || "Расходники фулфилмента", status: order.status === "PENDING" - ? "in-transit" + ? "planned" : order.status === "CONFIRMED" - ? "in-transit" + ? "confirmed" : order.status === "IN_TRANSIT" ? "in-transit" - : "available", + : order.status === "DELIVERED" + ? "in-stock" + : "planned", date: order.createdAt, supplier: order.partner.name || order.partner.fullName || "Не указан", minStock: Math.round(item.quantity * 0.1), @@ -3751,6 +3777,7 @@ export const resolvers = { totalItems: totalItems, organizationId: currentUser.organization.id, fulfillmentCenterId: fulfillmentCenterId, + logisticsPartnerId: args.input.logisticsPartnerId, status: initialStatus, items: { create: orderItems, @@ -3772,6 +3799,11 @@ export const resolvers = { users: true, }, }, + logisticsPartner: { + include: { + users: true, + }, + }, items: { include: { product: { @@ -3803,7 +3835,7 @@ export const resolvers = { quantity: item.quantity, unit: "шт", category: productWithCategory?.category?.name || "Расходники", - status: "in-transit", // Статус "в пути" так как заказ только создан + status: "planned", // Статус "запланировано" (ожидает одобрения поставщиком) date: new Date(args.input.deliveryDate), supplier: partner.name || partner.fullName || "Не указан", minStock: Math.round(item.quantity * 0.1), // 10% от заказанного как минимальный остаток @@ -5288,10 +5320,53 @@ export const resolvers = { }, }); - // Если статус изменился на DELIVERED, обновляем склад фулфилмента - if (args.status === "DELIVERED" && existingOrder.fulfillmentCenterId) { - console.log("🚚 Обновляем склад фулфилмента:", { + // Обновляем статусы расходников в зависимости от статуса заказа + const targetOrganizationId = existingOrder.fulfillmentCenterId || existingOrder.organizationId; + + if (args.status === "CONFIRMED") { + // При подтверждении поставщиком - переводим расходники в статус "confirmed" + await prisma.supply.updateMany({ + where: { + organizationId: targetOrganizationId, + status: "planned", + // Находим расходники по названиям товаров из заказа + name: { + in: existingOrder.items.map(item => item.product.name) + } + }, + data: { + status: "confirmed" + } + }); + + console.log("✅ Статусы расходников обновлены на 'confirmed'"); + } + + if (args.status === "IN_TRANSIT") { + // При отгрузке - переводим расходники в статус "in-transit" + await prisma.supply.updateMany({ + where: { + organizationId: targetOrganizationId, + status: "confirmed", + name: { + in: existingOrder.items.map(item => item.product.name) + } + }, + data: { + status: "in-transit" + } + }); + + console.log("✅ Статусы расходников обновлены на 'in-transit'"); + } + + // Если статус изменился на DELIVERED, обновляем склад + if (args.status === "DELIVERED") { + + console.log("🚚 Обновляем склад организации:", { + targetOrganizationId, fulfillmentCenterId: existingOrder.fulfillmentCenterId, + organizationId: existingOrder.organizationId, itemsCount: existingOrder.items.length, items: existingOrder.items.map((item) => ({ productName: item.product.name, @@ -5299,19 +5374,19 @@ export const resolvers = { })), }); - // Обновляем расходники фулфилмента + // Обновляем расходники for (const item of existingOrder.items) { console.log("📦 Обрабатываем товар:", { productName: item.product.name, quantity: item.quantity, - fulfillmentCenterId: existingOrder.fulfillmentCenterId, + targetOrganizationId, }); - // Ищем существующий расходник + // Ищем существующий расходник в правильной организации const existingSupply = await prisma.supply.findFirst({ where: { name: item.product.name, - organizationId: existingOrder.fulfillmentCenterId, + organizationId: targetOrganizationId, }, }); @@ -5329,14 +5404,14 @@ export const resolvers = { where: { id: existingSupply.id }, data: { currentStock: existingSupply.currentStock + item.quantity, - status: "available", // Меняем статус на "доступен" + status: "in-stock", // Меняем статус на "на складе" }, }); } else { console.log("➕ Создаем новый расходник:", { name: item.product.name, quantity: item.quantity, - organizationId: existingOrder.fulfillmentCenterId, + organizationId: targetOrganizationId, }); // Создаем новый расходник @@ -5350,7 +5425,7 @@ export const resolvers = { quantity: item.quantity, unit: "шт", category: item.product.category?.name || "Расходники", - status: "available", + status: "in-stock", date: new Date(), supplier: existingOrder.partner.name || @@ -5358,7 +5433,7 @@ export const resolvers = { "Не указан", minStock: Math.round(item.quantity * 0.1), currentStock: item.quantity, - organizationId: existingOrder.fulfillmentCenterId, + organizationId: targetOrganizationId, }, }); @@ -5370,7 +5445,7 @@ export const resolvers = { } } - console.log("🎉 Склад фулфилмента успешно обновлен!"); + console.log("🎉 Склад организации успешно обновлен!"); } return {