feat(supplier-orders): добавить параметры поставки в таблицу заявок
- Добавлены колонки Объём и Грузовые места между Цена товаров и Статус - Реализованы инпуты для ввода volume и packagesCount в статусе PENDING для роли WHOLESALE - Добавлена мутация UPDATE_SUPPLY_PARAMETERS с проверками безопасности - Скрыта строка Поставщик для роли WHOLESALE (поставщик знает свои данные) - Исправлено выравнивание таблицы при скрытии уровня поставщика - Реорганизованы документы: legacy-rules/, docs/, docs-and-reports/ ВНИМАНИЕ: Компонент multilevel-supplies-table.tsx (1697 строк) нарушает правило модульной архитектуры (>800 строк требует рефакторинга) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -15,6 +15,18 @@ import '@/lib/seed-init' // Автоматическая инициализац
|
||||
// 🔒 СИСТЕМА БЕЗОПАСНОСТИ - импорты
|
||||
import { CommercialDataAudit } from './security/commercial-data-audit'
|
||||
import { createSecurityContext } from './security/index'
|
||||
|
||||
// 🔒 HELPER: Создание безопасного контекста с организационными данными
|
||||
function createSecureContextWithOrgData(context: Context, currentUser: any) {
|
||||
return {
|
||||
...context,
|
||||
user: {
|
||||
...context.user,
|
||||
organizationType: currentUser.organization.type,
|
||||
organizationId: currentUser.organization.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
import { ParticipantIsolation } from './security/participant-isolation'
|
||||
import { SupplyDataFilter } from './security/supply-data-filter'
|
||||
import type { SecurityContext } from './security/types'
|
||||
@ -2735,15 +2747,17 @@ export const resolvers = {
|
||||
: []
|
||||
|
||||
// 🔒 ФИЛЬТРАЦИЯ РЕЦЕПТУРЫ ПО РОЛИ
|
||||
recipe = SupplyDataFilter.filterRecipeByRole(
|
||||
{
|
||||
// Для WHOLESALE скрываем рецептуру полностью
|
||||
if (currentUser.organization.type === 'WHOLESALE') {
|
||||
recipe = null
|
||||
} else {
|
||||
recipe = {
|
||||
services,
|
||||
fulfillmentConsumables,
|
||||
sellerConsumables,
|
||||
marketplaceCardId: item.marketplaceCardId,
|
||||
},
|
||||
securityContext,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@ -7269,6 +7283,67 @@ export const resolvers = {
|
||||
}
|
||||
},
|
||||
|
||||
// Обновление параметров поставки (объём и грузовые места)
|
||||
updateSupplyParameters: async (
|
||||
_: unknown,
|
||||
args: { id: string; volume?: number; packagesCount?: number },
|
||||
context: GraphQLContext
|
||||
) => {
|
||||
try {
|
||||
// Проверка аутентификации
|
||||
if (!context.user?.id) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Необходима аутентификация',
|
||||
}
|
||||
}
|
||||
|
||||
// Найти поставку и проверить права доступа
|
||||
const supply = await prisma.supplyOrder.findUnique({
|
||||
where: { id: args.id },
|
||||
include: { partner: true }
|
||||
})
|
||||
|
||||
if (!supply) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Поставка не найдена',
|
||||
}
|
||||
}
|
||||
|
||||
// Проверить, что пользователь - поставщик этой заявки
|
||||
if (supply.partnerId !== context.user.organization?.id) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Недостаточно прав для изменения данной поставки',
|
||||
}
|
||||
}
|
||||
|
||||
// Подготовить данные для обновления
|
||||
const updateData: { volume?: number; packagesCount?: number } = {}
|
||||
if (args.volume !== undefined) updateData.volume = args.volume
|
||||
if (args.packagesCount !== undefined) updateData.packagesCount = args.packagesCount
|
||||
|
||||
// Обновить поставку
|
||||
const updatedSupply = await prisma.supplyOrder.update({
|
||||
where: { id: args.id },
|
||||
data: updateData,
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Параметры поставки обновлены',
|
||||
order: updatedSupply,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка при обновлении параметров поставки:', error)
|
||||
return {
|
||||
success: false,
|
||||
message: 'Ошибка при обновлении параметров поставки',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Назначение логистики фулфилментом на заказ селлера
|
||||
assignLogisticsToSupply: async (
|
||||
_: unknown,
|
||||
@ -7543,22 +7618,34 @@ export const resolvers = {
|
||||
organization: true,
|
||||
},
|
||||
},
|
||||
recipe: {
|
||||
include: {
|
||||
services: true,
|
||||
fulfillmentConsumables: true,
|
||||
sellerConsumables: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
console.warn(`[DEBUG] updatedOrder structure:`, {
|
||||
id: updatedOrder.id,
|
||||
itemsCount: updatedOrder.items?.length || 0,
|
||||
firstItem: updatedOrder.items?.[0] ? {
|
||||
productId: updatedOrder.items[0].productId,
|
||||
hasProduct: !!updatedOrder.items[0].product,
|
||||
productOrgId: updatedOrder.items[0].product?.organizationId,
|
||||
hasProductOrg: !!updatedOrder.items[0].product?.organization,
|
||||
} : null,
|
||||
currentUserOrgId: currentUser.organization.id,
|
||||
})
|
||||
|
||||
// 🔒 ФИЛЬТРАЦИЯ ДАННЫХ ДЛЯ ПОСТАВЩИКА
|
||||
const filteredOrder = SupplyDataFilter.filterSupplyOrder(updatedOrder, securityContext)
|
||||
const securityContextWithOrgType = createSecureContextWithOrgData(context, currentUser)
|
||||
const filteredOrder = SupplyDataFilter.filterSupplyOrder(updatedOrder, securityContextWithOrgType)
|
||||
|
||||
console.warn(`[DEBUG] Заказ ${args.id} успешно обновлен до статуса: ${updatedOrder.status}`)
|
||||
console.warn(`[DEBUG] filteredOrder:`, {
|
||||
hasData: !!filteredOrder.data,
|
||||
dataId: filteredOrder.data?.id,
|
||||
dataKeys: Object.keys(filteredOrder.data || {}),
|
||||
})
|
||||
|
||||
try {
|
||||
const orgIds = [
|
||||
updatedOrder.organizationId,
|
||||
@ -7572,10 +7659,16 @@ export const resolvers = {
|
||||
})
|
||||
} catch {}
|
||||
|
||||
// Проверка на случай, если фильтрованные данные null
|
||||
if (!filteredOrder.data || !filteredOrder.data.id) {
|
||||
console.error('[ERROR] filteredOrder.data is null or missing id:', filteredOrder)
|
||||
throw new GraphQLError('Filtered order data is invalid')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Заказ поставки одобрен поставщиком. Товары зарезервированы, остатки обновлены.',
|
||||
order: filteredOrder, // 🔒 Возвращаем отфильтрованные данные
|
||||
order: filteredOrder.data, // 🔒 Возвращаем отфильтрованные данные (только data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error approving supply order:', error)
|
||||
@ -7689,20 +7782,14 @@ export const resolvers = {
|
||||
organization: true,
|
||||
},
|
||||
},
|
||||
recipe: {
|
||||
include: {
|
||||
services: true,
|
||||
fulfillmentConsumables: true,
|
||||
sellerConsumables: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// 🔒 ФИЛЬТРАЦИЯ ДАННЫХ ДЛЯ ПОСТАВЩИКА
|
||||
const filteredOrder = SupplyDataFilter.filterSupplyOrder(updatedOrder, securityContext)
|
||||
const securityContextWithOrgType = createSecureContextWithOrgData(context, currentUser)
|
||||
const filteredOrder = SupplyDataFilter.filterSupplyOrder(updatedOrder, securityContextWithOrgType)
|
||||
|
||||
// 📦 СНИМАЕМ РЕЗЕРВАЦИЮ ПРИ ОТКЛОНЕНИИ
|
||||
// Восстанавливаем остатки и убираем резервацию для каждого отклоненного товара
|
||||
@ -7756,7 +7843,7 @@ export const resolvers = {
|
||||
return {
|
||||
success: true,
|
||||
message: args.reason ? `Заказ отклонен поставщиком. Причина: ${args.reason}` : 'Заказ отклонен поставщиком',
|
||||
order: filteredOrder, // 🔒 Возвращаем отфильтрованные данные
|
||||
order: filteredOrder.data, // 🔒 Возвращаем отфильтрованные данные (только data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error rejecting supply order:', error)
|
||||
@ -7914,7 +8001,8 @@ export const resolvers = {
|
||||
})
|
||||
|
||||
// 🔒 ФИЛЬТРАЦИЯ ДАННЫХ ДЛЯ ПОСТАВЩИКА
|
||||
const filteredOrder = SupplyDataFilter.filterSupplyOrder(updatedOrder, securityContext)
|
||||
const securityContextWithOrgType = createSecureContextWithOrgData(context, currentUser)
|
||||
const filteredOrder = SupplyDataFilter.filterSupplyOrder(updatedOrder, securityContextWithOrgType)
|
||||
|
||||
try {
|
||||
const orgIds = [
|
||||
@ -7932,7 +8020,7 @@ export const resolvers = {
|
||||
return {
|
||||
success: true,
|
||||
message: "Заказ отправлен поставщиком. Товары переведены в статус 'в пути'.",
|
||||
order: filteredOrder, // 🔒 Возвращаем отфильтрованные данные
|
||||
order: filteredOrder.data, // 🔒 Возвращаем отфильтрованные данные (только data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error shipping supply order:', error)
|
||||
|
Reference in New Issue
Block a user