Добавлены новые поля в модель Supply и обновлены компоненты для работы с расходниками. Реализована логика загрузки и отображения чатов с непрочитанными сообщениями в мессенджере. Обновлены запросы и мутации GraphQL для поддержки новых полей. Исправлены ошибки отображения и добавлены индикаторы для непрочитанных сообщений.
This commit is contained in:
@ -533,9 +533,81 @@ export const resolvers = {
|
||||
throw new GraphQLError("У пользователя нет организации");
|
||||
}
|
||||
|
||||
// TODO: Здесь будет логика получения списка чатов
|
||||
// Пока возвращаем пустой массив, так как таблица сообщений еще не создана
|
||||
return [];
|
||||
// Получаем всех контрагентов
|
||||
const counterparties = await prisma.counterparty.findMany({
|
||||
where: { organizationId: currentUser.organization.id },
|
||||
include: {
|
||||
counterparty: {
|
||||
include: {
|
||||
users: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Для каждого контрагента получаем последнее сообщение и количество непрочитанных
|
||||
const conversations = await Promise.all(
|
||||
counterparties.map(async (cp) => {
|
||||
const counterpartyId = cp.counterparty.id;
|
||||
|
||||
// Последнее сообщение с этим контрагентом
|
||||
const lastMessage = await prisma.message.findFirst({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
senderOrganizationId: currentUser.organization!.id,
|
||||
receiverOrganizationId: counterpartyId,
|
||||
},
|
||||
{
|
||||
senderOrganizationId: counterpartyId,
|
||||
receiverOrganizationId: currentUser.organization!.id,
|
||||
},
|
||||
],
|
||||
},
|
||||
include: {
|
||||
sender: true,
|
||||
senderOrganization: {
|
||||
include: {
|
||||
users: true,
|
||||
},
|
||||
},
|
||||
receiverOrganization: {
|
||||
include: {
|
||||
users: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: { createdAt: "desc" },
|
||||
});
|
||||
|
||||
// Количество непрочитанных сообщений от этого контрагента
|
||||
const unreadCount = await prisma.message.count({
|
||||
where: {
|
||||
senderOrganizationId: counterpartyId,
|
||||
receiverOrganizationId: currentUser.organization!.id,
|
||||
isRead: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Если есть сообщения с этим контрагентом, включаем его в список
|
||||
if (lastMessage) {
|
||||
return {
|
||||
id: `${currentUser.organization!.id}-${counterpartyId}`,
|
||||
counterparty: cp.counterparty,
|
||||
lastMessage,
|
||||
unreadCount,
|
||||
updatedAt: lastMessage.createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
// Фильтруем null значения и сортируем по времени последнего сообщения
|
||||
return conversations
|
||||
.filter((conv) => conv !== null)
|
||||
.sort((a, b) => new Date(b!.updatedAt).getTime() - new Date(a!.updatedAt).getTime());
|
||||
},
|
||||
|
||||
// Мои услуги
|
||||
@ -2410,8 +2482,34 @@ export const resolvers = {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Здесь будет логика обновления статуса сообщений
|
||||
// Пока возвращаем успешный ответ
|
||||
const currentUser = await prisma.user.findUnique({
|
||||
where: { id: context.user.id },
|
||||
include: { organization: true },
|
||||
});
|
||||
|
||||
if (!currentUser?.organization) {
|
||||
throw new GraphQLError("У пользователя нет организации");
|
||||
}
|
||||
|
||||
// conversationId имеет формат "currentOrgId-counterpartyId"
|
||||
const [, counterpartyId] = args.conversationId.split('-');
|
||||
|
||||
if (!counterpartyId) {
|
||||
throw new GraphQLError("Неверный ID беседы");
|
||||
}
|
||||
|
||||
// Помечаем все непрочитанные сообщения от контрагента как прочитанные
|
||||
await prisma.message.updateMany({
|
||||
where: {
|
||||
senderOrganizationId: counterpartyId,
|
||||
receiverOrganizationId: currentUser.organization.id,
|
||||
isRead: false,
|
||||
},
|
||||
data: {
|
||||
isRead: true,
|
||||
},
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -2594,6 +2692,14 @@ export const resolvers = {
|
||||
name: string;
|
||||
description?: string;
|
||||
price: number;
|
||||
quantity: number;
|
||||
unit: string;
|
||||
category: string;
|
||||
status: string;
|
||||
date: string;
|
||||
supplier: string;
|
||||
minStock: number;
|
||||
currentStock: number;
|
||||
imageUrl?: string;
|
||||
};
|
||||
},
|
||||
@ -2627,7 +2733,14 @@ export const resolvers = {
|
||||
name: args.input.name,
|
||||
description: args.input.description,
|
||||
price: args.input.price,
|
||||
quantity: 0, // Временно устанавливаем 0, так как поле убрано из интерфейса
|
||||
quantity: args.input.quantity,
|
||||
unit: args.input.unit,
|
||||
category: args.input.category,
|
||||
status: args.input.status,
|
||||
date: new Date(args.input.date),
|
||||
supplier: args.input.supplier,
|
||||
minStock: args.input.minStock,
|
||||
currentStock: args.input.currentStock,
|
||||
imageUrl: args.input.imageUrl,
|
||||
organizationId: currentUser.organization.id,
|
||||
},
|
||||
@ -2657,6 +2770,14 @@ export const resolvers = {
|
||||
name: string;
|
||||
description?: string;
|
||||
price: number;
|
||||
quantity: number;
|
||||
unit: string;
|
||||
category: string;
|
||||
status: string;
|
||||
date: string;
|
||||
supplier: string;
|
||||
minStock: number;
|
||||
currentStock: number;
|
||||
imageUrl?: string;
|
||||
};
|
||||
},
|
||||
@ -2696,7 +2817,14 @@ export const resolvers = {
|
||||
name: args.input.name,
|
||||
description: args.input.description,
|
||||
price: args.input.price,
|
||||
quantity: 0, // Временно устанавливаем 0, так как поле убрано из интерфейса
|
||||
quantity: args.input.quantity,
|
||||
unit: args.input.unit,
|
||||
category: args.input.category,
|
||||
status: args.input.status,
|
||||
date: new Date(args.input.date),
|
||||
supplier: args.input.supplier,
|
||||
minStock: args.input.minStock,
|
||||
currentStock: args.input.currentStock,
|
||||
imageUrl: args.input.imageUrl,
|
||||
},
|
||||
include: { organization: true },
|
||||
@ -2912,9 +3040,37 @@ export const resolvers = {
|
||||
},
|
||||
});
|
||||
|
||||
// Создаем расходники на основе заказанных товаров
|
||||
const suppliesData = args.input.items.map((item) => {
|
||||
const product = products.find((p) => p.id === item.productId)!;
|
||||
const productWithCategory = supplyOrder.items.find(
|
||||
(orderItem) => orderItem.productId === item.productId
|
||||
)?.product;
|
||||
|
||||
return {
|
||||
name: product.name,
|
||||
description: product.description || `Заказано у ${partner.name}`,
|
||||
price: product.price,
|
||||
quantity: item.quantity,
|
||||
unit: "шт",
|
||||
category: productWithCategory?.category?.name || "Упаковка",
|
||||
status: "in-transit", // Статус "в пути" так как заказ только создан
|
||||
date: new Date(args.input.deliveryDate),
|
||||
supplier: partner.name || partner.fullName || "Не указан",
|
||||
minStock: Math.round(item.quantity * 0.1), // 10% от заказанного как минимальный остаток
|
||||
currentStock: 0, // Пока товар не пришел
|
||||
organizationId: currentUser.organization!.id,
|
||||
};
|
||||
});
|
||||
|
||||
// Создаем расходники
|
||||
await prisma.supply.createMany({
|
||||
data: suppliesData,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Заказ поставки создан успешно",
|
||||
message: `Заказ поставки создан успешно! Добавлено ${suppliesData.length} расходников в каталог.`,
|
||||
order: supplyOrder,
|
||||
};
|
||||
} catch (error) {
|
||||
@ -3000,7 +3156,7 @@ export const resolvers = {
|
||||
weight: args.input.weight,
|
||||
dimensions: args.input.dimensions,
|
||||
material: args.input.material,
|
||||
images: args.input.images || [],
|
||||
images: JSON.stringify(args.input.images || []),
|
||||
mainImage: args.input.mainImage,
|
||||
isActive: args.input.isActive ?? true,
|
||||
organizationId: currentUser.organization.id,
|
||||
@ -3111,7 +3267,7 @@ export const resolvers = {
|
||||
weight: args.input.weight,
|
||||
dimensions: args.input.dimensions,
|
||||
material: args.input.material,
|
||||
images: args.input.images || [],
|
||||
images: args.input.images ? JSON.stringify(args.input.images) : undefined,
|
||||
mainImage: args.input.mainImage,
|
||||
isActive: args.input.isActive ?? true,
|
||||
},
|
||||
@ -4213,6 +4369,25 @@ export const resolvers = {
|
||||
},
|
||||
},
|
||||
|
||||
Product: {
|
||||
images: (parent: { images: unknown }) => {
|
||||
// Если images это строка JSON, парсим её в массив
|
||||
if (typeof parent.images === 'string') {
|
||||
try {
|
||||
return JSON.parse(parent.images);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
// Если это уже массив, возвращаем как есть
|
||||
if (Array.isArray(parent.images)) {
|
||||
return parent.images;
|
||||
}
|
||||
// Иначе возвращаем пустой массив
|
||||
return [];
|
||||
},
|
||||
},
|
||||
|
||||
Message: {
|
||||
type: (parent: { type?: string | null }) => {
|
||||
return parent.type || "TEXT";
|
||||
|
Reference in New Issue
Block a user