Удален резервный файл employees-dashboard.tsx и добавлены новые функции для проверки уникальности артикула в форме продукта. Обновлены мутации GraphQL для поддержки проверки уникальности артикула, а также добавлены уведомления о низких остатках на складе. Оптимизирован интерфейс для улучшения пользовательского опыта.

This commit is contained in:
Bivekich
2025-08-01 12:10:48 +03:00
parent 52881cf302
commit 50b02f97b7
7 changed files with 566 additions and 804 deletions

View File

@ -4393,6 +4393,227 @@ export const resolvers = {
}
},
// Проверка уникальности артикула
checkArticleUniqueness: async (
_: unknown,
args: { article: string; excludeId?: string },
context: Context
) => {
const { currentUser, prisma } = context;
if (!currentUser?.organization?.id) {
return {
isUnique: false,
existingProduct: null,
};
}
try {
const existingProduct = await prisma.product.findFirst({
where: {
article: args.article,
organizationId: currentUser.organization.id,
...(args.excludeId && { id: { not: args.excludeId } }),
},
select: {
id: true,
name: true,
article: true,
},
});
return {
isUnique: !existingProduct,
existingProduct,
};
} catch (error) {
console.error("Error checking article uniqueness:", error);
return {
isUnique: false,
existingProduct: null,
};
}
},
// Резервирование товара при создании заказа
reserveProductStock: async (
_: unknown,
args: { productId: string; quantity: number },
context: Context
) => {
const { currentUser, prisma } = context;
if (!currentUser?.organization?.id) {
return {
success: false,
message: "Необходимо авторизоваться",
};
}
try {
const product = await prisma.product.findUnique({
where: { id: args.productId },
});
if (!product) {
return {
success: false,
message: "Товар не найден",
};
}
// Проверяем доступность товара
const availableStock = (product.stock || product.quantity) - (product.ordered || 0);
if (availableStock < args.quantity) {
return {
success: false,
message: `Недостаточно товара на складе. Доступно: ${availableStock}, запрошено: ${args.quantity}`,
};
}
// Резервируем товар (увеличиваем поле ordered)
const updatedProduct = await prisma.product.update({
where: { id: args.productId },
data: {
ordered: (product.ordered || 0) + args.quantity,
},
});
console.log(`📦 Зарезервировано ${args.quantity} единиц товара ${product.name}`);
return {
success: true,
message: `Зарезервировано ${args.quantity} единиц товара`,
product: updatedProduct,
};
} catch (error) {
console.error("Error reserving product stock:", error);
return {
success: false,
message: "Ошибка при резервировании товара",
};
}
},
// Освобождение резерва при отмене заказа
releaseProductReserve: async (
_: unknown,
args: { productId: string; quantity: number },
context: Context
) => {
const { currentUser, prisma } = context;
if (!currentUser?.organization?.id) {
return {
success: false,
message: "Необходимо авторизоваться",
};
}
try {
const product = await prisma.product.findUnique({
where: { id: args.productId },
});
if (!product) {
return {
success: false,
message: "Товар не найден",
};
}
// Освобождаем резерв (уменьшаем поле ordered)
const newOrdered = Math.max((product.ordered || 0) - args.quantity, 0);
const updatedProduct = await prisma.product.update({
where: { id: args.productId },
data: {
ordered: newOrdered,
},
});
console.log(`🔄 Освобожден резерв ${args.quantity} единиц товара ${product.name}`);
return {
success: true,
message: `Освобожден резерв ${args.quantity} единиц товара`,
product: updatedProduct,
};
} catch (error) {
console.error("Error releasing product reserve:", error);
return {
success: false,
message: "Ошибка при освобождении резерва",
};
}
},
// Обновление статуса "в пути"
updateProductInTransit: async (
_: unknown,
args: { productId: string; quantity: number; operation: string },
context: Context
) => {
const { currentUser, prisma } = context;
if (!currentUser?.organization?.id) {
return {
success: false,
message: "Необходимо авторизоваться",
};
}
try {
const product = await prisma.product.findUnique({
where: { id: args.productId },
});
if (!product) {
return {
success: false,
message: "Товар не найден",
};
}
let newInTransit = product.inTransit || 0;
let newOrdered = product.ordered || 0;
if (args.operation === "ship") {
// При отгрузке: переводим из "заказано" в "в пути"
newInTransit = (product.inTransit || 0) + args.quantity;
newOrdered = Math.max((product.ordered || 0) - args.quantity, 0);
} else if (args.operation === "deliver") {
// При доставке: убираем из "в пути", добавляем в "продано"
newInTransit = Math.max((product.inTransit || 0) - args.quantity, 0);
}
const updatedProduct = await prisma.product.update({
where: { id: args.productId },
data: {
inTransit: newInTransit,
ordered: newOrdered,
...(args.operation === "deliver" && {
sold: (product.sold || 0) + args.quantity,
}),
},
});
console.log(`🚚 Обновлен статус "в пути" для товара ${product.name}: ${args.operation}`);
return {
success: true,
message: `Статус товара обновлен: ${args.operation}`,
product: updatedProduct,
};
} catch (error) {
console.error("Error updating product in transit:", error);
return {
success: false,
message: "Ошибка при обновлении статуса товара",
};
}
},
// Удалить товар
deleteProduct: async (
_: unknown,
@ -5608,6 +5829,24 @@ export const resolvers = {
})),
});
// 🔄 СИНХРОНИЗАЦИЯ: Обновляем товары поставщика (переводим из "в пути" в "продано")
for (const item of existingOrder.items) {
const product = await prisma.product.findUnique({
where: { id: item.product.id },
});
if (product) {
await prisma.product.update({
where: { id: item.product.id },
data: {
inTransit: Math.max((product.inTransit || 0) - item.quantity, 0),
sold: (product.sold || 0) + item.quantity,
},
});
console.log(`✅ Товар поставщика "${product.name}" обновлен: доставлено ${item.quantity} единиц`);
}
}
// Обновляем расходники
for (const item of existingOrder.items) {
console.log("📦 Обрабатываем товар:", {
@ -5735,6 +5974,48 @@ export const resolvers = {
}
console.log(`[DEBUG] Поставщик ${currentUser.organization.name} одобряет заказ ${args.id}`);
// 🔄 СИНХРОНИЗАЦИЯ ОСТАТКОВ: Резервируем товары у поставщика
const orderWithItems = await prisma.supplyOrder.findUnique({
where: { id: args.id },
include: {
items: {
include: {
product: true,
},
},
},
});
if (orderWithItems) {
for (const item of orderWithItems.items) {
// Резервируем товар (увеличиваем поле ordered)
const product = await prisma.product.findUnique({
where: { id: item.product.id },
});
if (product) {
const availableStock = (product.stock || product.quantity) - (product.ordered || 0);
if (availableStock < item.quantity) {
return {
success: false,
message: `Недостаточно товара "${product.name}" на складе. Доступно: ${availableStock}, требуется: ${item.quantity}`,
};
}
await prisma.product.update({
where: { id: item.product.id },
data: {
ordered: (product.ordered || 0) + item.quantity,
},
});
console.log(`📦 Зарезервировано ${item.quantity} единиц товара "${product.name}"`);
}
}
}
const updatedOrder = await prisma.supplyOrder.update({
where: { id: args.id },
data: { status: "SUPPLIER_APPROVED" },
@ -5759,7 +6040,7 @@ export const resolvers = {
console.log(`[DEBUG] Заказ ${args.id} успешно обновлен до статуса: ${updatedOrder.status}`);
return {
success: true,
message: "Заказ поставки одобрен поставщиком",
message: "Заказ поставки одобрен поставщиком. Товары зарезервированы.",
order: updatedOrder,
};
} catch (error) {
@ -5880,6 +6161,38 @@ export const resolvers = {
};
}
// 🔄 СИНХРОНИЗАЦИЯ ОСТАТКОВ: Переводим товары из "заказано" в "в пути"
const orderWithItems = await prisma.supplyOrder.findUnique({
where: { id: args.id },
include: {
items: {
include: {
product: true,
},
},
},
});
if (orderWithItems) {
for (const item of orderWithItems.items) {
const product = await prisma.product.findUnique({
where: { id: item.product.id },
});
if (product) {
await prisma.product.update({
where: { id: item.product.id },
data: {
ordered: Math.max((product.ordered || 0) - item.quantity, 0),
inTransit: (product.inTransit || 0) + item.quantity,
},
});
console.log(`🚚 Товар "${product.name}" переведен в статус "в пути": ${item.quantity} единиц`);
}
}
}
const updatedOrder = await prisma.supplyOrder.update({
where: { id: args.id },
data: { status: "SHIPPED" },
@ -5903,7 +6216,7 @@ export const resolvers = {
return {
success: true,
message: "Заказ отправлен поставщиком",
message: "Заказ отправлен поставщиком. Товары переведены в статус 'в пути'.",
order: updatedOrder,
};
} catch (error) {