feat(v2-inventory): мигрировать систему расходников на V2 архитектуру

Переход от старой таблицы Supply к новой FulfillmentConsumableInventory:

- Обновлен mySupplies resolver для чтения из V2 таблицы с корректными остатками
- Добавлена V2 мутация updateFulfillmentInventoryPrice для обновления цен
- Исправлен counterpartySupplies для показа актуальных V2 цен в рецептурах
- Frontend использует новую мутацию UPDATE_FULFILLMENT_INVENTORY_PRICE
- Цены расходников корректно сохраняются и отображаются после перезагрузки
- Селлеры видят правильные цены при создании поставок товаров

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-25 22:26:29 +03:00
parent f3285d9139
commit a48efb8757
4 changed files with 580 additions and 97 deletions

View File

@ -203,7 +203,9 @@ export const typeDefs = gql`
deleteService(id: ID!): Boolean!
# Работа с расходниками (только обновление цены разрешено)
# DEPRECATED: используйте updateFulfillmentInventoryPrice
updateSupplyPrice(id: ID!, input: UpdateSupplyPriceInput!): SupplyResponse!
updateFulfillmentInventoryPrice(id: ID!, input: UpdateSupplyPriceInput!): FulfillmentInventoryResponse!
# Использование расходников фулфилмента
useFulfillmentSupplies(input: UseFulfillmentSuppliesInput!): SupplyResponse!
@ -653,6 +655,28 @@ export const typeDefs = gql`
pricePerUnit: Float # Может быть null (цена не установлена)
}
# V2 типы для инвентаря фулфилмента
type FulfillmentInventoryItem {
id: ID!
name: String!
description: String
pricePerUnit: Float # Цена перепродажи
unit: String!
imageUrl: String
warehouseStock: Int!
isAvailable: Boolean!
warehouseConsumableId: ID!
createdAt: DateTime!
updatedAt: DateTime!
organization: Organization!
}
type FulfillmentInventoryResponse {
success: Boolean!
message: String!
item: FulfillmentInventoryItem
}
input UseFulfillmentSuppliesInput {
supplyId: ID!
quantityUsed: Int!
@ -1734,6 +1758,7 @@ export const typeDefs = gql`
# Input типы для создания поставок
input CreateFulfillmentConsumableSupplyInput {
supplierId: ID!
logisticsPartnerId: ID # Логистический партнер (опционально)
requestedDeliveryDate: DateTime!
items: [FulfillmentConsumableSupplyItemInput!]!
notes: String
@ -1744,6 +1769,19 @@ export const typeDefs = gql`
requestedQuantity: Int!
}
# Input для приемки поставки
input ReceiveFulfillmentConsumableSupplyInput {
supplyOrderId: ID!
items: [ReceiveFulfillmentConsumableSupplyItemInput!]!
notes: String
}
input ReceiveFulfillmentConsumableSupplyItemInput {
productId: ID!
receivedQuantity: Int!
defectQuantity: Int
}
# Response типы
type CreateFulfillmentConsumableSupplyResult {
success: Boolean!
@ -1751,11 +1789,18 @@ export const typeDefs = gql`
supplyOrder: FulfillmentConsumableSupplyOrder
}
type SupplierConsumableSupplyResponse {
success: Boolean!
message: String!
order: FulfillmentConsumableSupplyOrder
}
# Расширяем Query и Mutation для новой системы
extend type Query {
# Новые запросы для системы поставок v2
myFulfillmentConsumableSupplies: [FulfillmentConsumableSupplyOrder!]!
mySupplierConsumableSupplies: [FulfillmentConsumableSupplyOrder!]!
myLogisticsConsumableSupplies: [FulfillmentConsumableSupplyOrder!]!
fulfillmentConsumableSupply(id: ID!): FulfillmentConsumableSupplyOrder
}
@ -1764,5 +1809,162 @@ export const typeDefs = gql`
createFulfillmentConsumableSupply(
input: CreateFulfillmentConsumableSupplyInput!
): CreateFulfillmentConsumableSupplyResult!
# Приемка поставки с автоматическим обновлением инвентаря
receiveFulfillmentConsumableSupply(
input: ReceiveFulfillmentConsumableSupplyInput!
): CreateFulfillmentConsumableSupplyResult!
# Мутации поставщика для V2 расходников фулфилмента
supplierApproveConsumableSupply(id: ID!): SupplierConsumableSupplyResponse!
supplierRejectConsumableSupply(id: ID!, reason: String): SupplierConsumableSupplyResponse!
supplierShipConsumableSupply(id: ID!): SupplierConsumableSupplyResponse!
# Мутации логистики для V2 расходников фулфилмента
logisticsConfirmConsumableSupply(id: ID!): SupplierConsumableSupplyResponse!
logisticsRejectConsumableSupply(id: ID!, reason: String): SupplierConsumableSupplyResponse!
# Мутация фулфилмента для приемки V2 расходников
fulfillmentReceiveConsumableSupply(
id: ID!
items: [ReceiveFulfillmentConsumableSupplyItemInput!]!
notes: String
): SupplierConsumableSupplyResponse!
}
# =============================================================================
# 📦 СИСТЕМА ПОСТАВОК РАСХОДНИКОВ СЕЛЛЕРА
# =============================================================================
# 5-статусная система для поставок селлера
enum SellerSupplyOrderStatus {
PENDING # Ожидает одобрения поставщика
APPROVED # Одобрено поставщиком
SHIPPED # Отгружено
DELIVERED # Доставлено
COMPLETED # Завершено
CANCELLED # Отменено
}
# Основной тип для поставки расходников селлера
type SellerConsumableSupplyOrder {
id: ID!
status: SellerSupplyOrderStatus!
# Данные селлера (создатель)
sellerId: ID!
seller: Organization!
fulfillmentCenterId: ID!
fulfillmentCenter: Organization!
requestedDeliveryDate: DateTime!
notes: String
# Данные поставщика
supplierId: ID
supplier: Organization
supplierApprovedAt: DateTime
packagesCount: Int
estimatedVolume: Float
supplierContractId: String
supplierNotes: String
# Данные логистики
logisticsPartnerId: ID
logisticsPartner: Organization
estimatedDeliveryDate: DateTime
routeId: ID
logisticsCost: Float
logisticsNotes: String
# Данные отгрузки
shippedAt: DateTime
trackingNumber: String
# Данные приемки
receivedAt: DateTime
receivedById: ID
receivedBy: User
actualQuantity: Int
defectQuantity: Int
receiptNotes: String
# Экономика (для будущего раздела экономики)
totalCostWithDelivery: Float
estimatedStorageCost: Float
items: [SellerConsumableSupplyItem!]!
createdAt: DateTime!
updatedAt: DateTime!
}
# Позиция в поставке селлера
type SellerConsumableSupplyItem {
id: ID!
productId: ID!
product: Product!
requestedQuantity: Int!
approvedQuantity: Int
shippedQuantity: Int
receivedQuantity: Int
defectQuantity: Int
unitPrice: Float!
totalPrice: Float!
createdAt: DateTime!
updatedAt: DateTime!
}
# Input типы для создания поставок селлера
input CreateSellerConsumableSupplyInput {
fulfillmentCenterId: ID! # куда доставлять (FULFILLMENT партнер)
supplierId: ID! # от кого заказывать (WHOLESALE партнер)
logisticsPartnerId: ID # кто везет (LOGIST партнер, опционально)
requestedDeliveryDate: DateTime! # когда нужно
items: [SellerConsumableSupplyItemInput!]!
notes: String
}
input SellerConsumableSupplyItemInput {
productId: ID! # какой расходник заказываем
requestedQuantity: Int! # сколько нужно
}
# Response типы для селлера
type CreateSellerConsumableSupplyResult {
success: Boolean!
message: String!
supplyOrder: SellerConsumableSupplyOrder
}
# Расширяем Query для селлерских поставок
extend type Query {
# Поставки селлера (мои заказы)
mySellerConsumableSupplies: [SellerConsumableSupplyOrder!]!
# Входящие заказы от селлеров (для фулфилмента)
incomingSellerSupplies: [SellerConsumableSupplyOrder!]!
# Поставки селлеров для поставщиков
mySellerSupplyRequests: [SellerConsumableSupplyOrder!]!
# Конкретная поставка селлера
sellerConsumableSupply(id: ID!): SellerConsumableSupplyOrder
}
# Расширяем Mutation для селлерских поставок
extend type Mutation {
# Создание поставки расходников селлера
createSellerConsumableSupply(
input: CreateSellerConsumableSupplyInput!
): CreateSellerConsumableSupplyResult!
# Обновление статуса поставки (для поставщиков и фулфилмента)
updateSellerSupplyStatus(
id: ID!
status: SellerSupplyOrderStatus!
notes: String
): SellerConsumableSupplyOrder!
# Отмена поставки селлером (только PENDING/APPROVED)
cancelSellerSupply(id: ID!): SellerConsumableSupplyOrder!
}
`