feat(graphql): обновить систему на V2 архитектуру с улучшенной синхронизацией

- Переход с таблицы Supply на fulfillmentConsumableInventory для V2 системы
- Добавить productId в GraphQL схему Supply для корректной фильтрации
- Обновить резолверы статистики фулфилмента для использования V2 таблиц
- Исправить подсчёт ежедневных изменений с использованием V2 поставок
- Добавить синхронизацию данных между связанными компонентами

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-27 12:27:26 +03:00
parent b405daa1be
commit 0fe8a7072d
3 changed files with 60 additions and 30 deletions

View File

@ -171,6 +171,7 @@ export const GET_MY_FULFILLMENT_SUPPLIES = gql`
currentStock currentStock
usedStock usedStock
imageUrl imageUrl
productId
createdAt createdAt
updatedAt updatedAt
} }

View File

@ -13,7 +13,9 @@ import { WildberriesService } from '@/services/wildberries-service'
import '@/lib/seed-init' // Автоматическая инициализация БД import '@/lib/seed-init' // Автоматическая инициализация БД
// Импорт новых resolvers для системы поставок v2 // Импорт новых resolvers для системы поставок v2
import { fulfillmentConsumableV2Queries, fulfillmentConsumableV2Mutations } from './resolvers/fulfillment-consumables-v2' import { fulfillmentConsumableV2Queries, fulfillmentConsumableV2Mutations } from './resolvers/fulfillment-consumables-v2'
import { fulfillmentConsumableV2Queries as fulfillmentConsumableV2QueriesRestored, fulfillmentConsumableV2Mutations as fulfillmentConsumableV2MutationsRestored } from './resolvers/fulfillment-consumables-v2-restored'
import { fulfillmentInventoryV2Queries } from './resolvers/fulfillment-inventory-v2' import { fulfillmentInventoryV2Queries } from './resolvers/fulfillment-inventory-v2'
import { logisticsConsumableV2Queries, logisticsConsumableV2Mutations } from './resolvers/logistics-consumables-v2'
import { CommercialDataAudit } from './security/commercial-data-audit' import { CommercialDataAudit } from './security/commercial-data-audit'
import { createSecurityContext } from './security/index' import { createSecurityContext } from './security/index'
@ -864,7 +866,8 @@ export const resolvers = {
return [] return []
}, },
// Расходники фулфилмента из склада (новая архитектура - синхронизация со склада) // ЗАКОММЕНТИРОВАНО: Старый resolver заменен на V2 из fulfillment-inventory-v2.ts
/*
myFulfillmentSupplies: async (_: unknown, __: unknown, context: Context) => { myFulfillmentSupplies: async (_: unknown, __: unknown, context: Context) => {
console.warn('🔥🔥🔥 FULFILLMENT SUPPLIES RESOLVER CALLED (NEW ARCHITECTURE) 🔥🔥🔥') console.warn('🔥🔥🔥 FULFILLMENT SUPPLIES RESOLVER CALLED (NEW ARCHITECTURE) 🔥🔥🔥')
@ -963,6 +966,7 @@ export const resolvers = {
organizationId: item.fulfillmentCenterId, organizationId: item.fulfillmentCenterId,
})) }))
}, },
*/
// Заказы поставок расходников // Заказы поставок расходников
supplyOrders: async (_: unknown, __: unknown, context: Context) => { supplyOrders: async (_: unknown, __: unknown, context: Context) => {
@ -1252,17 +1256,18 @@ export const resolvers = {
console.warn(`🏭 FULFILLMENT SUPPLY ORDERS: ${fulfillmentSupplyOrders.length}`) console.warn(`🏭 FULFILLMENT SUPPLY ORDERS: ${fulfillmentSupplyOrders.length}`)
// Подсчитываем количество из таблицы Supply (актуальные остатки на складе фулфилмента) // V2 СИСТЕМА: Подсчитываем количество из fulfillmentConsumableInventory
// ИСПРАВЛЕНО: считаем только расходники фулфилмента, исключаем расходники селлеров const fulfillmentSuppliesFromWarehouse = await prisma.fulfillmentConsumableInventory.findMany({
const fulfillmentSuppliesFromWarehouse = await prisma.supply.findMany({
where: { where: {
organizationId: organizationId, // Склад фулфилмента fulfillmentCenterId: organizationId, // Склад фулфилмента
type: 'FULFILLMENT_CONSUMABLES', // ТОЛЬКО расходники фулфилмента },
include: {
product: true,
}, },
}) })
const fulfillmentSuppliesCount = fulfillmentSuppliesFromWarehouse.reduce( const fulfillmentSuppliesCount = fulfillmentSuppliesFromWarehouse.reduce(
(sum, supply) => sum + (supply.currentStock || 0), (sum, item) => sum + (item.currentStock || 0),
0, 0,
) )
@ -1270,20 +1275,20 @@ export const resolvers = {
`🔥 FULFILLMENT SUPPLIES DEBUG: organizationId=${organizationId}, ordersCount=${fulfillmentSupplyOrders.length}, warehouseCount=${fulfillmentSuppliesFromWarehouse.length}, totalStock=${fulfillmentSuppliesCount}`, `🔥 FULFILLMENT SUPPLIES DEBUG: organizationId=${organizationId}, ordersCount=${fulfillmentSupplyOrders.length}, warehouseCount=${fulfillmentSuppliesFromWarehouse.length}, totalStock=${fulfillmentSuppliesCount}`,
) )
console.warn( console.warn(
'📦 FULFILLMENT SUPPLIES BREAKDOWN:', '📦 FULFILLMENT SUPPLIES BREAKDOWN (V2):',
fulfillmentSuppliesFromWarehouse.map((supply) => ({ fulfillmentSuppliesFromWarehouse.map((item) => ({
name: supply.name, name: item.product.name,
currentStock: supply.currentStock, currentStock: item.currentStock,
supplier: supply.supplier, totalReceived: item.totalReceived,
productId: item.product.id,
})), })),
) )
// Изменения расходников фулфилмента за сутки (ПРИБЫЛО) // V2 СИСТЕМА: Изменения расходников фулфилмента за сутки (ПРИБЫЛО)
// Ищем заказы фулфилмента, доставленные на его склад за последние сутки // Ищем поставки расходников V2, обновлённые за последние сутки
const fulfillmentSuppliesReceivedToday = await prisma.supplyOrder.findMany({ const fulfillmentSuppliesReceivedTodayV2 = await prisma.fulfillmentConsumableSupplyOrder.findMany({
where: { where: {
organizationId: organizationId, // Заказчик = фулфилмент fulfillmentCenterId: organizationId,
fulfillmentCenterId: organizationId, // ИСПРАВЛЕНО: доставлено НА склад фулфилмента
status: 'DELIVERED', status: 'DELIVERED',
updatedAt: { gte: oneDayAgo }, updatedAt: { gte: oneDayAgo },
}, },
@ -1294,15 +1299,15 @@ export const resolvers = {
}, },
}) })
const fulfillmentSuppliesChangeToday = fulfillmentSuppliesReceivedToday.reduce( const fulfillmentSuppliesChangeToday = fulfillmentSuppliesReceivedTodayV2.reduce(
(sum, order) => (sum, supplyOrder) =>
sum + sum +
order.items.reduce((itemSum, item) => itemSum + (item.product.type === 'CONSUMABLE' ? item.quantity : 0), 0), supplyOrder.items.reduce((itemSum, item) => itemSum + (item.receivedQuantity || 0), 0),
0, 0,
) )
console.warn( console.warn(
`📊 FULFILLMENT SUPPLIES RECEIVED TODAY (ПРИБЫЛО): ${fulfillmentSuppliesReceivedToday.length} orders, ${fulfillmentSuppliesChangeToday} items`, `📊 FULFILLMENT SUPPLIES RECEIVED TODAY V2 (ПРИБЫЛО): ${fulfillmentSuppliesReceivedTodayV2.length} orders, ${fulfillmentSuppliesChangeToday} items`,
) )
// Расходники селлеров - получаем из таблицы Supply (актуальные остатки на складе фулфилмента) // Расходники селлеров - получаем из таблицы Supply (актуальные остатки на складе фулфилмента)
@ -2844,8 +2849,11 @@ export const resolvers = {
} }
}, },
// Новая система поставок v2 // ВОССТАНОВЛЕННАЯ система поставок v2 из бэкапов
...fulfillmentConsumableV2Queries, ...fulfillmentConsumableV2QueriesRestored,
// V2 система поставок для логистики
...logisticsConsumableV2Queries,
// Новая система складских остатков V2 (заменяет старый myFulfillmentSupplies) // Новая система складских остатков V2 (заменяет старый myFulfillmentSupplies)
...fulfillmentInventoryV2Queries, ...fulfillmentInventoryV2Queries,
@ -10491,8 +10499,11 @@ resolvers.Mutation = {
} }
}, },
// Добавляем v2 mutations через spread // ВОССТАНОВЛЕННЫЕ v2 mutations из бэкапов
...fulfillmentConsumableV2Mutations, ...fulfillmentConsumableV2MutationsRestored,
// V2 mutations для логистики
...logisticsConsumableV2Mutations,
} }
/* // Резолвер для парсинга JSON рецептуры в SupplyOrderItem /* // Резолвер для парсинга JSON рецептуры в SupplyOrderItem

View File

@ -610,6 +610,7 @@ export const typeDefs = gql`
type Supply { type Supply {
id: ID! id: ID!
productId: ID # ID продукта для фильтрации истории поставок
name: String! name: String!
article: String! # ДОБАВЛЕНО: Артикул СФ для уникальности article: String! # ДОБАВЛЕНО: Артикул СФ для уникальности
description: String description: String
@ -1777,7 +1778,7 @@ export const typeDefs = gql`
} }
input ReceiveFulfillmentConsumableSupplyItemInput { input ReceiveFulfillmentConsumableSupplyItemInput {
productId: ID! id: ID!
receivedQuantity: Int! receivedQuantity: Int!
defectQuantity: Int defectQuantity: Int
} }
@ -1820,10 +1821,6 @@ export const typeDefs = gql`
supplierRejectConsumableSupply(id: ID!, reason: String): SupplierConsumableSupplyResponse! supplierRejectConsumableSupply(id: ID!, reason: String): SupplierConsumableSupplyResponse!
supplierShipConsumableSupply(id: ID!): SupplierConsumableSupplyResponse! supplierShipConsumableSupply(id: ID!): SupplierConsumableSupplyResponse!
# Мутации логистики для V2 расходников фулфилмента
logisticsConfirmConsumableSupply(id: ID!): SupplierConsumableSupplyResponse!
logisticsRejectConsumableSupply(id: ID!, reason: String): SupplierConsumableSupplyResponse!
# Мутация фулфилмента для приемки V2 расходников # Мутация фулфилмента для приемки V2 расходников
fulfillmentReceiveConsumableSupply( fulfillmentReceiveConsumableSupply(
id: ID! id: ID!
@ -1967,4 +1964,25 @@ export const typeDefs = gql`
# Отмена поставки селлером (только PENDING/APPROVED) # Отмена поставки селлером (только PENDING/APPROVED)
cancelSellerSupply(id: ID!): SellerConsumableSupplyOrder! cancelSellerSupply(id: ID!): SellerConsumableSupplyOrder!
} }
# === V2 ЛОГИСТИКА РАСХОДНИКОВ ФУЛФИЛМЕНТА ===
extend type Query {
# Получить V2 поставки для логистической компании
myLogisticsConsumableSupplies: [FulfillmentConsumableSupplyOrder!]!
}
extend type Mutation {
# Подтверждение поставки логистикой
logisticsConfirmConsumableSupply(id: ID!): LogisticsConsumableSupplyResponse!
# Отклонение поставки логистикой
logisticsRejectConsumableSupply(id: ID!, reason: String): LogisticsConsumableSupplyResponse!
}
# Ответ логистических операций V2
type LogisticsConsumableSupplyResponse {
success: Boolean!
message: String!
order: FulfillmentConsumableSupplyOrder
}
` `