feat: реализовать полную автосинхронизацию V2 системы расходников с nameForSeller и анализ миграции
- ✅ Добавлено поле nameForSeller в FulfillmentConsumable для кастомизации названий - ✅ Добавлено поле inventoryId для связи между каталогом и складом - ✅ Реализована автосинхронизация FulfillmentConsumableInventory → FulfillmentConsumable - ✅ Обновлен UI с колонкой "Название для селлера" в /fulfillment/services/consumables - ✅ Исправлены GraphQL запросы (удалено поле description, добавлены новые поля) - ✅ Создан скрипт sync-inventory-to-catalog.ts для миграции существующих данных - ✅ Добавлена техническая документация архитектуры системы инвентаря - ✅ Создан отчет о статусе миграции V1→V2 с детальным планом 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -12,6 +12,7 @@ import { WildberriesService } from '@/services/wildberries-service'
|
||||
|
||||
import { fulfillmentConsumableV2Queries as fulfillmentConsumableV2QueriesRestored, fulfillmentConsumableV2Mutations as fulfillmentConsumableV2MutationsRestored } from './resolvers/fulfillment-consumables-v2-restored'
|
||||
import { fulfillmentInventoryV2Queries } from './resolvers/fulfillment-inventory-v2'
|
||||
import { fulfillmentServicesQueries, fulfillmentServicesMutations } from './resolvers/fulfillment-services-v2'
|
||||
import { sellerGoodsQueries, sellerGoodsMutations } from './resolvers/goods-supply-v2'
|
||||
import { logisticsConsumableV2Queries, logisticsConsumableV2Mutations } from './resolvers/logistics-consumables-v2'
|
||||
import { sellerConsumableQueries, sellerConsumableMutations } from './resolvers/seller-consumables'
|
||||
@ -2170,40 +2171,39 @@ export const resolvers = {
|
||||
throw new GraphQLError('Расходники доступны только у фулфилмент центров')
|
||||
}
|
||||
|
||||
// Получаем расходники из V2 инвентаря фулфилмента с правильными ценами
|
||||
const inventoryItems = await prisma.fulfillmentConsumableInventory.findMany({
|
||||
// V2 СИСТЕМА А: Получаем расходники из каталога услуг фулфилмента
|
||||
const consumables = await prisma.fulfillmentConsumable.findMany({
|
||||
where: {
|
||||
fulfillmentCenterId: args.organizationId,
|
||||
currentStock: { gt: 0 }, // Только те, что есть в наличии
|
||||
resalePrice: { not: null }, // Только те, у которых установлена цена
|
||||
fulfillmentId: args.organizationId,
|
||||
isAvailable: true,
|
||||
pricePerUnit: { gt: 0 }, // Только с установленными ценами для селлеров
|
||||
},
|
||||
include: {
|
||||
product: true,
|
||||
fulfillmentCenter: true,
|
||||
fulfillment: true,
|
||||
},
|
||||
orderBy: { lastSupplyDate: 'desc' },
|
||||
orderBy: { sortOrder: 'asc' },
|
||||
})
|
||||
|
||||
console.warn('🔥 COUNTERPARTY SUPPLIES - V2 FORMAT:', {
|
||||
console.warn('🔥 COUNTERPARTY SUPPLIES - V2 СИСТЕМА А:', {
|
||||
organizationId: args.organizationId,
|
||||
itemsCount: inventoryItems.length,
|
||||
itemsWithPrices: inventoryItems.filter(item => item.resalePrice).length,
|
||||
consumablesCount: consumables.length,
|
||||
consumablesWithPrices: consumables.filter(c => c.pricePerUnit > 0).length,
|
||||
})
|
||||
|
||||
// Преобразуем V2 формат в формат старого Supply для обратной совместимости
|
||||
return inventoryItems.map((item) => ({
|
||||
id: item.id,
|
||||
name: item.product.name,
|
||||
description: item.product.description || '',
|
||||
price: item.resalePrice ? parseFloat(item.resalePrice.toString()) : 0, // Цена перепродажи из V2
|
||||
quantity: item.currentStock, // Текущий остаток
|
||||
unit: 'шт', // TODO: добавить unit в Product модель
|
||||
// Преобразуем V2 Система А в формат V1 для обратной совместимости
|
||||
return consumables.map((consumable) => ({
|
||||
id: consumable.id,
|
||||
name: consumable.nameForSeller || consumable.name, // Используем nameForSeller если установлено
|
||||
description: consumable.description || '',
|
||||
price: parseFloat(consumable.pricePerUnit.toString()), // Цена для селлеров из Системы А
|
||||
quantity: consumable.currentStock, // Текущий остаток
|
||||
unit: consumable.unit, // Единица измерения
|
||||
category: 'CONSUMABLE',
|
||||
status: 'AVAILABLE',
|
||||
imageUrl: item.product.mainImage,
|
||||
createdAt: item.createdAt,
|
||||
updatedAt: item.updatedAt,
|
||||
organization: item.fulfillmentCenter,
|
||||
imageUrl: consumable.imageUrl,
|
||||
createdAt: consumable.createdAt,
|
||||
updatedAt: consumable.updatedAt,
|
||||
organization: consumable.fulfillment,
|
||||
}))
|
||||
},
|
||||
|
||||
@ -2916,6 +2916,9 @@ export const resolvers = {
|
||||
// Новая система складских остатков V2 (заменяет старый myFulfillmentSupplies)
|
||||
...fulfillmentInventoryV2Queries,
|
||||
|
||||
// V2 система услуг фулфилмента (включая myFulfillmentConsumables)
|
||||
...fulfillmentServicesQueries,
|
||||
|
||||
// V2 система складских остатков расходников селлера
|
||||
...sellerInventoryV2Queries,
|
||||
|
||||
@ -4861,6 +4864,8 @@ export const resolvers = {
|
||||
},
|
||||
context: Context,
|
||||
) => {
|
||||
console.warn('🔥 UPDATE_SUPPLY_PRICE called with args:', args)
|
||||
|
||||
if (!context.user) {
|
||||
throw new GraphQLError('Требуется авторизация', {
|
||||
extensions: { code: 'UNAUTHENTICATED' },
|
||||
@ -4882,48 +4887,46 @@ export const resolvers = {
|
||||
}
|
||||
|
||||
try {
|
||||
// Находим и обновляем расходник в V2 таблице FulfillmentConsumableInventory
|
||||
const existingInventoryItem = await prisma.fulfillmentConsumableInventory.findFirst({
|
||||
// V2 СИСТЕМА А: Находим и обновляем расходник в каталоге услуг фулфилмента
|
||||
const existingConsumable = await prisma.fulfillmentConsumable.findFirst({
|
||||
where: {
|
||||
id: args.id,
|
||||
fulfillmentCenterId: currentUser.organization.id,
|
||||
fulfillmentId: currentUser.organization.id,
|
||||
},
|
||||
include: {
|
||||
product: true,
|
||||
fulfillmentCenter: true,
|
||||
fulfillment: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (!existingInventoryItem) {
|
||||
throw new GraphQLError('Расходник не найден в инвентаре')
|
||||
if (!existingConsumable) {
|
||||
throw new GraphQLError('Расходник не найден в каталоге услуг')
|
||||
}
|
||||
|
||||
const updatedInventoryItem = await prisma.fulfillmentConsumableInventory.update({
|
||||
const updatedConsumable = await prisma.fulfillmentConsumable.update({
|
||||
where: { id: args.id },
|
||||
data: {
|
||||
resalePrice: args.input.pricePerUnit, // Обновляем цену перепродажи в V2
|
||||
pricePerUnit: args.input.pricePerUnit || 0, // Обновляем цену для селлеров в V2 Система А
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
include: {
|
||||
product: true,
|
||||
fulfillmentCenter: true,
|
||||
fulfillment: true,
|
||||
},
|
||||
})
|
||||
|
||||
// Преобразуем V2 данные в формат для GraphQL (аналогично mySupplies resolver)
|
||||
// Преобразуем V2 данные в формат для GraphQL
|
||||
const transformedSupply = {
|
||||
id: updatedInventoryItem.id,
|
||||
name: updatedInventoryItem.product.name,
|
||||
description: updatedInventoryItem.product.description || '',
|
||||
pricePerUnit: updatedInventoryItem.resalePrice ? parseFloat(updatedInventoryItem.resalePrice.toString()) : null,
|
||||
unit: 'шт', // TODO: добавить unit в Product модель
|
||||
imageUrl: updatedInventoryItem.product.mainImage,
|
||||
warehouseStock: updatedInventoryItem.currentStock,
|
||||
isAvailable: updatedInventoryItem.currentStock > 0,
|
||||
warehouseConsumableId: updatedInventoryItem.id,
|
||||
createdAt: updatedInventoryItem.createdAt,
|
||||
updatedAt: updatedInventoryItem.updatedAt,
|
||||
organization: updatedInventoryItem.fulfillmentCenter,
|
||||
id: updatedConsumable.id,
|
||||
name: updatedConsumable.name,
|
||||
description: updatedConsumable.description || '',
|
||||
pricePerUnit: parseFloat(updatedConsumable.pricePerUnit.toString()),
|
||||
unit: updatedConsumable.unit,
|
||||
imageUrl: updatedConsumable.imageUrl,
|
||||
warehouseStock: updatedConsumable.currentStock,
|
||||
isAvailable: updatedConsumable.isAvailable,
|
||||
warehouseConsumableId: updatedConsumable.id,
|
||||
createdAt: updatedConsumable.createdAt,
|
||||
updatedAt: updatedConsumable.updatedAt,
|
||||
organization: updatedConsumable.fulfillment,
|
||||
}
|
||||
|
||||
console.warn('🔥 V2 SUPPLY PRICE UPDATED:', {
|
||||
@ -4978,34 +4981,33 @@ export const resolvers = {
|
||||
}
|
||||
|
||||
try {
|
||||
const updatedInventoryItem = await prisma.fulfillmentConsumableInventory.update({
|
||||
const updatedConsumable = await prisma.fulfillmentConsumable.update({
|
||||
where: {
|
||||
id: args.id,
|
||||
fulfillmentCenterId: currentUser.organization.id,
|
||||
fulfillmentId: currentUser.organization.id,
|
||||
},
|
||||
data: {
|
||||
resalePrice: args.input.pricePerUnit,
|
||||
pricePerUnit: args.input.pricePerUnit || 0,
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
include: {
|
||||
product: true,
|
||||
fulfillmentCenter: true,
|
||||
fulfillment: true,
|
||||
},
|
||||
})
|
||||
|
||||
const transformedItem = {
|
||||
id: updatedInventoryItem.id,
|
||||
name: updatedInventoryItem.product.name,
|
||||
description: updatedInventoryItem.product.description || '',
|
||||
pricePerUnit: updatedInventoryItem.resalePrice ? parseFloat(updatedInventoryItem.resalePrice.toString()) : null,
|
||||
unit: 'шт',
|
||||
imageUrl: updatedInventoryItem.product.mainImage,
|
||||
warehouseStock: updatedInventoryItem.currentStock,
|
||||
isAvailable: updatedInventoryItem.currentStock > 0,
|
||||
warehouseConsumableId: updatedInventoryItem.id,
|
||||
createdAt: updatedInventoryItem.createdAt,
|
||||
updatedAt: updatedInventoryItem.updatedAt,
|
||||
organization: updatedInventoryItem.fulfillmentCenter,
|
||||
id: updatedConsumable.id,
|
||||
name: updatedConsumable.name,
|
||||
description: updatedConsumable.description || '',
|
||||
pricePerUnit: parseFloat(updatedConsumable.pricePerUnit.toString()),
|
||||
unit: updatedConsumable.unit,
|
||||
imageUrl: updatedConsumable.imageUrl,
|
||||
warehouseStock: updatedConsumable.currentStock,
|
||||
isAvailable: updatedConsumable.isAvailable,
|
||||
warehouseConsumableId: updatedConsumable.id,
|
||||
createdAt: updatedConsumable.createdAt,
|
||||
updatedAt: updatedConsumable.updatedAt,
|
||||
organization: updatedConsumable.fulfillment,
|
||||
}
|
||||
|
||||
console.warn('🔥 V2 FULFILLMENT INVENTORY PRICE UPDATED:', {
|
||||
@ -10310,6 +10312,9 @@ resolvers.Mutation = {
|
||||
// V2 mutations для поставок расходников селлера
|
||||
...sellerConsumableMutations,
|
||||
|
||||
// V2 mutations для услуг фулфилмента
|
||||
...fulfillmentServicesMutations,
|
||||
|
||||
// V2 mutations для товарных поставок селлера
|
||||
...sellerGoodsMutations,
|
||||
}
|
||||
|
Reference in New Issue
Block a user