Files
apollo-backend/scripts/seed.js
2026-05-16 17:16:31 +07:00

315 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dotenv/config';
import { prisma } from '../src/prisma-client.js';
const managerEmail = 'manager@fregat.local';
const clientEmail = 'client@fregat.local';
const PRODUCT_TYPES = {
STANDARD: 'стандартная лента',
PAINTING: 'малярный скотч',
};
const PRODUCT_ROWS = [
{ sku: '480200', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 40, thicknessMicron: 38, sleeveBrand: 'фрегат', quantityPerBox: '72' },
{ sku: '481200', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 55, thicknessMicron: 38, sleeveBrand: 'фрегат', quantityPerBox: '72/36' },
{ sku: '482200', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 66, thicknessMicron: 38, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '483200', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 120, thicknessMicron: 38, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '751200', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 55, thicknessMicron: 38, sleeveBrand: 'фрегат', quantityPerBox: '48' },
{ sku: '752200', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 66, thicknessMicron: 38, sleeveBrand: 'фрегат', quantityPerBox: '24' },
{ sku: '753200', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 120, thicknessMicron: 38, sleeveBrand: 'фрегат', quantityPerBox: '24' },
{ sku: '481400', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 55, thicknessMicron: 43, sleeveBrand: 'фрегат', quantityPerBox: '72/36' },
{ sku: '482400', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 66, thicknessMicron: 43, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '483400', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 120, thicknessMicron: 43, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '751400', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 55, thicknessMicron: 43, sleeveBrand: 'фрегат', quantityPerBox: '48' },
{ sku: '752400', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 66, thicknessMicron: 43, sleeveBrand: 'фрегат', quantityPerBox: '24' },
{ sku: '753400', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 120, thicknessMicron: 43, sleeveBrand: 'фрегат', quantityPerBox: '24' },
{ sku: '481500', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 55, thicknessMicron: 45, sleeveBrand: 'фрегат', quantityPerBox: '72/36' },
{ sku: '482500', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 66, thicknessMicron: 45, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '483500', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 120, thicknessMicron: 45, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '487500', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 150, thicknessMicron: 45, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '751500', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 55, thicknessMicron: 45, sleeveBrand: 'фрегат', quantityPerBox: '48' },
{ sku: '752500', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 66, thicknessMicron: 45, sleeveBrand: 'фрегат', quantityPerBox: '24' },
{ sku: '743500', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 120, thicknessMicron: 45, sleeveBrand: 'фрегат', quantityPerBox: '24' },
{ sku: '482600', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 66, thicknessMicron: 47, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '483600', productType: PRODUCT_TYPES.STANDARD, widthMm: 48, lengthM: 120, thicknessMicron: 47, sleeveBrand: 'фрегат', quantityPerBox: '36' },
{ sku: '752600', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 66, thicknessMicron: 47, sleeveBrand: 'фрегат', quantityPerBox: '24' },
{ sku: '753600', productType: PRODUCT_TYPES.STANDARD, widthMm: 75, lengthM: 120, thicknessMicron: 47, sleeveBrand: 'фрегат', quantityPerBox: '24' },
];
function assertUniqueProductRows(rows) {
const seen = new Map();
for (const row of rows) {
const signature = [
row.productType,
row.widthMm,
row.lengthM,
row.thicknessMicron,
row.sleeveBrand,
].join('|');
const duplicate = seen.get(signature);
if (duplicate) {
throw new Error(
`Duplicate product signature detected for ${row.sku} and ${duplicate.sku}: ${signature}`,
);
}
seen.set(signature, row);
}
}
function sentenceCase(value) {
if (!value) {
return value;
}
return value[0].toUpperCase() + value.slice(1);
}
function buildName(product) {
return [
sentenceCase(product.productType),
`${product.widthMm}x${product.lengthM} м`,
`${product.thicknessMicron} мкм`,
`втулка ${sentenceCase(product.sleeveBrand)}`,
`короб ${product.quantityPerBox}`,
].join(', ');
}
function baseQuantity(quantityPerBox) {
const firstValue = quantityPerBox.split('/')[0];
const parsed = Number.parseInt(firstValue, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : 24;
}
const company = await prisma.company.upsert({
where: { inn: '7701000000' },
update: {},
create: {
name: 'Fregat Client LLC',
inn: '7701000000',
},
});
const manager = await prisma.user.upsert({
where: { email: managerEmail },
update: { fullName: 'Default Manager' },
create: {
email: managerEmail,
fullName: 'Default Manager',
role: 'MANAGER',
},
});
await prisma.user.upsert({
where: { email: clientEmail },
update: {
fullName: 'Demo Client',
companyId: company.id,
bonusProgramEnabled: true,
},
create: {
email: clientEmail,
fullName: 'Demo Client',
role: 'CLIENT',
bonusProgramEnabled: true,
companyId: company.id,
},
});
const warehouseMain = await prisma.warehouse.upsert({
where: { code: 'MSK-01' },
update: { name: 'Main warehouse' },
create: { code: 'MSK-01', name: 'Main warehouse' },
});
const warehouseReserve = await prisma.warehouse.upsert({
where: { code: 'SPB-01' },
update: { name: 'Reserve warehouse' },
create: { code: 'SPB-01', name: 'Reserve warehouse' },
});
const activeSkus = PRODUCT_ROWS.map((item) => item.sku);
assertUniqueProductRows(PRODUCT_ROWS);
await prisma.product.updateMany({
where: { sku: { notIn: activeSkus } },
data: { isActive: false },
});
for (const product of PRODUCT_ROWS) {
const qtyBase = baseQuantity(product.quantityPerBox);
const qtyMain = qtyBase * 10;
const qtyReserve = qtyBase * 5;
const dbProduct = await prisma.product.upsert({
where: { sku: product.sku },
update: {
name: buildName(product),
description: `Артикул: ${product.sku}`,
productType: product.productType,
widthMm: product.widthMm,
lengthM: product.lengthM,
thicknessMicron: product.thicknessMicron,
sleeveBrand: product.sleeveBrand,
quantityPerBox: product.quantityPerBox,
isCustomizable: false,
isActive: true,
},
create: {
sku: product.sku,
name: buildName(product),
description: `Артикул: ${product.sku}`,
productType: product.productType,
widthMm: product.widthMm,
lengthM: product.lengthM,
thicknessMicron: product.thicknessMicron,
sleeveBrand: product.sleeveBrand,
quantityPerBox: product.quantityPerBox,
isCustomizable: false,
isActive: true,
},
});
await prisma.productStock.upsert({
where: {
productId_warehouseId: {
productId: dbProduct.id,
warehouseId: warehouseMain.id,
},
},
update: { availableQty: qtyMain },
create: {
productId: dbProduct.id,
warehouseId: warehouseMain.id,
availableQty: qtyMain,
},
});
await prisma.productStock.upsert({
where: {
productId_warehouseId: {
productId: dbProduct.id,
warehouseId: warehouseReserve.id,
},
},
update: { availableQty: qtyReserve },
create: {
productId: dbProduct.id,
warehouseId: warehouseReserve.id,
availableQty: qtyReserve,
},
});
}
const client = await prisma.user.findUnique({
where: { email: clientEmail },
});
if (!client) {
throw new Error('Demo client was not created.');
}
const seededProducts = await prisma.product.findMany({
where: {
sku: {
in: ['480200', '482500', '753400', '752600'],
},
},
orderBy: { sku: 'asc' },
});
const existingOrderCodes = new Set(
(await prisma.order.findMany({
where: {
code: {
in: ['FRG-1001', 'FRG-1002', 'FRG-1003'],
},
},
select: { code: true },
})).map((order) => order.code),
);
const demoOrders = [
{
code: 'FRG-1001',
status: 'WAITING_DOUBLE_CONFIRM',
deliveryAddress: 'Москва, Ленинградский проспект, 37',
deliveryTerms: 'Самовывоз со склада после подтверждения',
totalPrice: '125000.00',
items: [
{ sku: '480200', quantity: '24.000' },
{ sku: '752600', quantity: '12.000' },
],
},
{
code: 'FRG-1002',
status: 'IN_PROGRESS',
deliveryAddress: 'Санкт-Петербург, Кубинская улица, 75к1',
deliveryTerms: 'Доставка транспортной компанией',
totalPrice: '84200.00',
items: [
{ sku: '482500', quantity: '18.000' },
],
},
{
code: 'FRG-1003',
status: 'COMPLETED',
deliveryAddress: 'Казань, улица Родины, 43',
deliveryTerms: 'Доставка до адреса клиента',
totalPrice: '156400.00',
items: [
{ sku: '753400', quantity: '30.000' },
{ sku: '482500', quantity: '10.000' },
],
},
];
for (const demoOrder of demoOrders) {
if (existingOrderCodes.has(demoOrder.code)) {
continue;
}
const order = await prisma.order.create({
data: {
code: demoOrder.code,
kind: 'READY',
status: demoOrder.status,
customerId: client.id,
managerId: manager.id,
deliveryAddress: demoOrder.deliveryAddress,
deliveryTerms: demoOrder.deliveryTerms,
totalPrice: demoOrder.totalPrice,
},
});
for (const item of demoOrder.items) {
const product = seededProducts.find((entry) => entry.sku === item.sku);
if (!product) {
throw new Error(`Product ${item.sku} not found for demo order ${demoOrder.code}.`);
}
await prisma.orderItem.create({
data: {
orderId: order.id,
productId: product.id,
productName: product.name,
quantity: item.quantity,
},
});
}
await prisma.orderStatusEvent.create({
data: {
orderId: order.id,
status: demoOrder.status,
actorUserId: manager.id,
note: 'Демо-заказ для отображения клиентского кабинета.',
},
});
}
console.log(`Seed complete with ${PRODUCT_ROWS.length} products. Use manager header x-user-id: ${manager.id}`);
await prisma.$disconnect();