import { Contact } from '../entities/crm/contact'; import { Company } from '../entities/crm/company'; import { Deal, DealStage } from '../entities/crm/deal'; import { Order, OrderStatus, PaymentStatus, FulfillmentStatus } from '../entities/commerce/order'; import { Product, ProductCategory } from '../entities/commerce/product'; import { Warehouse } from '../entities/inventory/warehouse'; import { StockItem, StockMovement } from '../services/contracts/inventory'; import { Account, CashFlowTransaction } from '../services/contracts/finance'; import { Payment, Shipment } from '../services/contracts/commerce'; import { SYSTEM_ENTITY_METADATA } from '../entities/metadata/registry'; import { AuditEntry } from '../services/contracts/audit'; import { putRecord, putStoreItem, countRecords, resetDatabase, } from '../services/storage/indexedDb'; import { DemoClock } from './demoClock'; function relDate(daysOffset: number, hoursOffset = 0): string { const base = DemoClock.getNow().getTime(); const target = new Date(base + daysOffset * 86400000 + hoursOffset * 3600000); return target.toISOString(); } function relDateStr(daysOffset: number): string { return relDate(daysOffset).slice(0, 10); } // ---------------------------------------------------- // 1. COMPANIES (12) // ---------------------------------------------------- export function generateSeedCompanies(): Company[] { const raw = [ { name: 'ООО «СеверТорг»', inn: '7701234567', roles: ['client', 'partner'] as const, city: 'Москва' }, { name: 'АО «ТехноЛаб»', inn: '7812987654', roles: ['vendor'] as const, city: 'Санкт-Петербург' }, { name: 'ГК «Айсберг Логистик»', inn: '5405112233', roles: ['partner'] as const, city: 'Новосибирск' }, { name: 'ООО «Кофе Эксперт»', inn: '7722445566', roles: ['vendor', 'client'] as const, city: 'Москва' }, { name: 'ИП «Михайлов Автодеталь»', inn: '6677889900', roles: ['client'] as const, city: 'Екатеринбург' }, { name: 'ООО «Бьюти Групп»', inn: '7733557799', roles: ['client'] as const, city: 'Москва' }, { name: 'АО «Ресторанный Синдикат»', inn: '7801334455', roles: ['client', 'partner'] as const, city: 'Санкт-Петербург' }, { name: 'ООО «ПакТрейд Упаковка»', inn: '5001223344', roles: ['vendor'] as const, city: 'Мытищи' }, { name: 'ООО «СтройСервисМонтаж»', inn: '7744112233', roles: ['subcontractor'] as const, city: 'Москва' }, { name: 'ИП «Семенова Студио»', inn: '2311445566', roles: ['client'] as const, city: 'Краснодар' }, { name: 'ООО «Гурман Продукт»', inn: '7715667788', roles: ['vendor'] as const, city: 'Москва' }, { name: 'АО «ФармаХолдинг»', inn: '7819998877', roles: ['client'] as const, city: 'Санкт-Петербург' }, ]; return raw.map((c, i) => ({ id: `comp_seed_${i + 1}`, name: c.name, legalName: c.name, inn: c.inn, kpp: '770101001', roles: [...c.roles], phone: `+7 (495) ${100 + i * 15}-${10 + i * 2}-${20 + i * 3}`, email: `contact@${c.name.toLowerCase().replace(/[^a-z0-9]/g, '') || 'company'}.ru`, website: `https://${c.name.toLowerCase().replace(/[^a-z0-9]/g, '') || 'company'}.example.com`, address: `г. ${c.city}, ул. Деловая, д. ${i + 10}`, status: 'active', audit: { createdAt: relDate(-30 + i), createdById: 'usr_sys_01', updatedAt: relDate(-2), updatedById: 'usr_sys_01', version: 1, }, })); } // ---------------------------------------------------- // 2. CONTACTS (32) // ---------------------------------------------------- export function generateSeedContacts(companies: Company[]): Contact[] { const names = [ ['Алексей', 'Морозов', 'Владимирович'], ['Елена', 'Соколова', 'Сергеевна'], ['Михаил', 'Романов', 'Николаевич'], ['Анна', 'Кузнецова', 'Игоревна'], ['Дмитрий', 'Смирнов', 'Алексеевич'], ['Ольга', 'Попова', 'Дмитриевна'], ['Сергей', 'Васильев', 'Петрович'], ['Татьяна', 'Новикова', 'Олеговна'], ['Иван', 'Федоров', 'Сергеевич'], ['Наталья', 'Морозова', 'Андреевна'], ['Артем', 'Волков', 'Михайлович'], ['Мария', 'Алексеева', 'Юрьевна'], ['Виктор', 'Лебедев', 'Борисович'], ['Юлия', 'Семенова', 'Викторовна'], ['Павел', 'Егоров', 'Анатольевич'], ['Ирина', 'Павлова', 'Константиновна'], ['Роман', 'Козлов', 'Евгеньевич'], ['Екатерина', 'Степанова', 'Валерьевна'], ['Денис', 'Николаев', 'Григорьевич'], ['Светлана', 'Орлова', 'Романовна'], ['Константин', 'Андреев', 'Сергеевич'], ['Алина', 'Макарова', 'Ильинична'], ['Максим', 'Захаров', 'Витальевич'], ['Ксения', 'Зайцева', 'Тимуровна'], ['Владимир', 'Соловьев', 'Максимович'], ['Дарья', 'Борисова', 'Денисовна'], ['Андрей', 'Яковлев', 'Станиславович'], ['Виктория', 'Григорьева', 'Аркадьевна'], ['Илья', 'Романов', 'Вадимович'], ['Полина', 'Воробьева', 'Антоновна'], ['Григорий', 'Сергеев', 'Олегович'], ['Александра', 'Кузьмина', 'Федоровна'], ]; return names.map(([first, last, middle], i) => { const comp = companies[i % companies.length]; const role = i % 4 === 0 ? 'partner' : i % 3 === 0 ? 'vendor' : 'client'; return { id: `cnt_seed_${i + 1}`, firstName: first, lastName: last, middleName: middle, roles: [role], companyId: i % 2 === 0 ? comp.id : undefined, jobTitle: i % 2 === 0 ? 'Руководитель направления' : 'Частный клиент', email: `${last.toLowerCase()}.${first.toLowerCase()}@example.com`, phone: `+7 (9${10 + (i % 80)}) ${200 + i * 5}-${10 + (i % 80)}-${20 + (i % 70)}`, telegram: `@${last.toLowerCase()}_${i + 1}`, status: 'active', notes: `Клиентский профиль #${i + 1}. Приоритетное обслуживание.`, audit: { createdAt: relDate(-25 + i * 0.5), createdById: 'usr_sys_01', updatedAt: relDate(-1), updatedById: 'usr_sys_01', version: 1, }, }; }); } // ---------------------------------------------------- // 3. CATEGORIES & PRODUCTS (45 across 6 categories) // ---------------------------------------------------- export function generateSeedCategories(): ProductCategory[] { const cats = [ { id: 'cat_beverages', name: 'Кофе и чайное сырье', code: 'BEV' }, { id: 'cat_food', name: 'Кулинария и десерты', code: 'FOOD' }, { id: 'cat_equip', name: 'Кофейное оборудование', code: 'EQUIP' }, { id: 'cat_auto', name: 'Автохимия и запчасти', code: 'AUTO' }, { id: 'cat_beauty', name: 'Косметика и уход', code: 'BEAUTY' }, { id: 'cat_services', name: 'Сервисные работы и ТО', code: 'SRV' }, ]; return cats.map((c) => ({ id: c.id, name: c.name, code: c.code, audit: { createdAt: relDate(-60), createdById: 'usr_sys_01', updatedAt: relDate(-10), updatedById: 'usr_sys_01', version: 1, }, })); } export function generateSeedProducts(categories: ProductCategory[]): Product[] { const products: Product[] = []; const baseSkus = [ // Beverages (8) { cat: 'cat_beverages', name: 'Зерновой кофе Espresso Bar 1кг', price: 185000, cost: 95000, unit: 'кг' }, { cat: 'cat_beverages', name: 'Зерновой кофе Ethiopia Yirgacheffe 1кг', price: 245000, cost: 130000, unit: 'кг' }, { cat: 'cat_beverages', name: 'Зерновой кофе Colombia Supremo 1кг', price: 210000, cost: 110000, unit: 'кг' }, { cat: 'cat_beverages', name: 'Чай листовой Ассам Мокалбари 500г', price: 120000, cost: 60000, unit: 'уп' }, { cat: 'cat_beverages', name: 'Чай зеленый Сенча Премиум 500г', price: 135000, cost: 70000, unit: 'уп' }, { cat: 'cat_beverages', name: 'Сироп Карамель Соленая 1л', price: 89000, cost: 42000, unit: 'бут' }, { cat: 'cat_beverages', name: 'Сироп Ваниль Мадагаскар 1л', price: 89000, cost: 42000, unit: 'бут' }, { cat: 'cat_beverages', name: 'Какао-порошок Barry Callebaut 1кг', price: 160000, cost: 90000, unit: 'кг' }, // Food (8) { cat: 'cat_food', name: 'Круассан классический на сливочном масле', price: 22000, cost: 9000, unit: 'шт' }, { cat: 'cat_food', name: 'Круассан миндальный с кремом', price: 29000, cost: 12000, unit: 'шт' }, { cat: 'cat_food', name: 'Сэндвич с лососем и авокадо', price: 42000, cost: 21000, unit: 'шт' }, { cat: 'cat_food', name: 'Сэндвич с индейкой и песто', price: 36000, cost: 17000, unit: 'шт' }, { cat: 'cat_food', name: 'Чизкейк Нью-Йорк классический', price: 34000, cost: 14000, unit: 'порц' }, { cat: 'cat_food', name: 'Торт Морковный со сливочным кремом', price: 32000, cost: 13000, unit: 'порц' }, { cat: 'cat_food', name: 'Гранола с греческим йогуртом и ягодами', price: 28000, cost: 11000, unit: 'порц' }, { cat: 'cat_food', name: 'Печенье Кукис Шоколадное 80г', price: 15000, cost: 6000, unit: 'шт' }, // Equipment (7) { cat: 'cat_equip', name: 'Кофемашина La Marzocco Linea PB 2GR', price: 98000000, cost: 72000000, unit: 'шт' }, { cat: 'cat_equip', name: 'Кофемашина Nuova Simonelli Appia Life 2GR', price: 45000000, cost: 34000000, unit: 'шт' }, { cat: 'cat_equip', name: 'Кофемолка Mahlkonig E65S GBW', price: 23000000, cost: 17500000, unit: 'шт' }, { cat: 'cat_equip', name: 'Водоумягчитель DVA 12л', price: 1450000, cost: 850000, unit: 'шт' }, { cat: 'cat_equip', name: 'Темпер динамометрический 58.5мм', price: 95000, cost: 45000, unit: 'шт' }, { cat: 'cat_equip', name: 'Питчер молочный Motta 500мл', price: 38000, cost: 18000, unit: 'шт' }, { cat: 'cat_equip', name: 'Весы бариста Acaia Lunar', price: 280000, cost: 190000, unit: 'шт' }, // Auto (8) { cat: 'cat_auto', name: 'Моторное масло синтетическое 5W-40 4л', price: 450000, cost: 270000, unit: 'канистра' }, { cat: 'cat_auto', name: 'Моторное масло 0W-20 Eco 4л', price: 520000, cost: 310000, unit: 'канистра' }, { cat: 'cat_auto', name: 'Масляный фильтр Mann-Filter W712', price: 85000, cost: 42000, unit: 'шт' }, { cat: 'cat_auto', name: 'Воздушный фильтр C25004', price: 95000, cost: 48000, unit: 'шт' }, { cat: 'cat_auto', name: 'Колодки тормозные передние керамик', price: 420000, cost: 230000, unit: 'компл' }, { cat: 'cat_auto', name: 'Тормозная жидкость DOT-4 1л', price: 75000, cost: 35000, unit: 'бут' }, { cat: 'cat_auto', name: 'Очиститель тормозов аэрозоль 500мл', price: 45000, cost: 20000, unit: 'баллон' }, { cat: 'cat_auto', name: 'Антифриз готовый красный G12+ 5кг', price: 160000, cost: 85000, unit: 'канистра' }, // Beauty (7) { cat: 'cat_beauty', name: 'Шампунь восстанавливающий кератин 1л', price: 320000, cost: 160000, unit: 'бут' }, { cat: 'cat_beauty', name: 'Маска глубокого увлажнения волос 500мл', price: 280000, cost: 135000, unit: 'банка' }, { cat: 'cat_beauty', name: 'Масло аргановое для кончиков волос 100мл', price: 210000, cost: 95000, unit: 'флакон' }, { cat: 'cat_beauty', name: 'Крем питательный для рук с маслом ши 75мл', price: 85000, cost: 38000, unit: 'тюбик' }, { cat: 'cat_beauty', name: 'Гель-лак премиум оттенок Nude 15мл', price: 65000, cost: 28000, unit: 'флакон' }, { cat: 'cat_beauty', name: 'Сыворотка для лица с гиалуроновой кислотой 30мл', price: 390000, cost: 180000, unit: 'флакон' }, { cat: 'cat_beauty', name: 'Набор одноразовых пилок для маникюра (50 шт)', price: 95000, cost: 40000, unit: 'уп' }, // Services (7) { cat: 'cat_services', name: 'Регламентное ТО кофемашины с декальцинацией', price: 650000, cost: 200000, unit: 'усл', isSrv: true }, { cat: 'cat_services', name: 'Комплексная замена масла и фильтров автомобиля', price: 250000, cost: 80000, unit: 'усл', isSrv: true }, { cat: 'cat_services', name: 'Диагностика ходовой части автомобиля на вибростенде', price: 180000, cost: 50000, unit: 'усл', isSrv: true }, { cat: 'cat_services', name: 'Стрижка женская модельная и укладка', price: 350000, cost: 100000, unit: 'усл', isSrv: true }, { cat: 'cat_services', name: 'Маникюр аппаратный с покрытием гель-лак', price: 280000, cost: 80000, unit: 'усл', isSrv: true }, { cat: 'cat_services', name: 'Настройка и калибровка кофемолки bar-flow', price: 300000, cost: 90000, unit: 'усл', isSrv: true }, { cat: 'cat_services', name: 'Выездной аудит торговой точки и склада', price: 1200000, cost: 400000, unit: 'усл', isSrv: true }, ]; baseSkus.forEach((b, i) => { const isService = !!b.isSrv; const cat = categories.find((c) => c.id === b.cat); products.push({ id: `prd_seed_${i + 1}`, sku: `${cat?.code || 'GEN'}-${1000 + i}`, name: b.name, categoryId: b.cat, categoryName: cat?.name || 'Общая', barcode: `4600000${100000 + i}`, price: { minor: b.price, currency: 'RUB' }, costPrice: { minor: b.cost, currency: 'RUB' }, unit: b.unit, isService, trackInventory: !isService, minStockAlert: isService ? 0 : 10, status: 'active', audit: { createdAt: relDate(-45 + (i % 20)), createdById: 'usr_sys_01', updatedAt: relDate(-2), updatedById: 'usr_sys_01', version: 1, }, }); }); return products; } // ---------------------------------------------------- // 4. WAREHOUSES (3) // ---------------------------------------------------- export function generateSeedWarehouses(): Warehouse[] { return [ { id: 'wh_main', name: 'Центральный распределительный склад (Москва-Юг)', code: 'WH-MAIN', type: 'central', address: 'г. Москва, Варшавское шоссе, д. 148', isDefault: true, status: 'active', responsibleUserId: 'usr_wh_01', audit: { createdAt: relDate(-100), createdById: 'usr_sys_01', updatedAt: relDate(-5), updatedById: 'usr_sys_01', version: 1, }, }, { id: 'wh_northwest', name: 'Региональный склад (Санкт-Петербург СЗ)', code: 'WH-NW', type: 'store', address: 'г. Санкт-Петербург, Выборгское шоссе, д. 32', isDefault: false, status: 'active', responsibleUserId: 'usr_wh_02', audit: { createdAt: relDate(-80), createdById: 'usr_sys_01', updatedAt: relDate(-5), updatedById: 'usr_sys_01', version: 1, }, }, { id: 'wh_showroom', name: 'Склад-шоурум (Флагманский магазин)', code: 'WH-SHOWROOM', type: 'store', address: 'г. Москва, ул. Большая Дмитровка, д. 7', isDefault: false, status: 'active', responsibleUserId: 'usr_wh_03', audit: { createdAt: relDate(-60), createdById: 'usr_sys_01', updatedAt: relDate(-5), updatedById: 'usr_sys_01', version: 1, }, }, ]; } // ---------------------------------------------------- // 5. DEALS (25 across pipeline stages) // ---------------------------------------------------- export function generateSeedDeals(contacts: Contact[], companies: Company[]): Deal[] { const stages: DealStage[] = ['discovery', 'proposal', 'negotiation', 'contract', 'won', 'lost']; const titles = [ 'Поставка кофемашин для сети ресторанов', 'Годовой сервисный контракт на автопарк', 'Оптовая партия премиального зернового кофе', 'Оснащение спа-салона уходовой косметикой', 'Комплекс ТО и замена масел для таксопарка', 'Поставка оборудования для 3 новых кофеен', 'Партия сиропов и чайных смесей для бара', 'Договор на поставку автохимии и фильтров', 'Корпоративные подарочные наборы кофе и чая', 'Обновление кухонного и барного инвентаря', 'Запуск франшизной точки: полное оснащение', 'Поставка косметики для сети барбершопов', 'Договор планового технического обслуживания', 'Спецзаказ на партию динамических темперов', 'Поставка расходных материалов для шиномонтажа', 'Кейтеринг и мобильная кофейня на выставку', 'Автоматизация рабочих мест и кассовой зоны', 'Поставка профессиональных весов и посуды', 'Партия синтетических масел 0W-20 для СТО', 'Поставка десертов и выпечки для кофе-поинтов', 'Сертификация и аудит оборудования фабрики', 'Поставка средств для бережного ухода за кожей', 'Замена группы прокладок и клапанов 12 машин', 'Снабжение автосервиса тормозными колодками', 'Партия крафтового какао для кондитерской', ]; return titles.map((title, i) => { const stage = stages[i % stages.length]; const amountMinor = 15000000 + ((i * 37) % 85) * 1000000; // between 150,000 and 1,000,000 RUB const contact = contacts[i % contacts.length]; const company = companies[i % companies.length]; const prob = stage === 'won' ? 100 : stage === 'lost' ? 0 : stage === 'contract' ? 85 : stage === 'negotiation' ? 65 : 40; return { id: `deal_seed_${i + 1}`, title, pipelineId: 'pipe_b2b', stage, amount: { minor: amountMinor, currency: 'RUB' }, probability: prob, expectedCloseDate: relDateStr(5 + (i % 25)), contactId: contact.id, companyId: company.id, lostReason: stage === 'lost' ? 'Бюджет проекта перенесен на следующий квартал' : undefined, audit: { createdAt: relDate(-20 + (i % 15)), createdById: 'usr_sys_01', updatedAt: relDate(-1), updatedById: 'usr_sys_01', version: 1, }, }; }); } // ---------------------------------------------------- // 6. ORDERS (28 with complete items, payments, shipments) // ---------------------------------------------------- export function generateSeedOrders( contacts: Contact[], products: Product[], warehouses: Warehouse[] ): { orders: Order[]; payments: Payment[]; shipments: Shipment[] } { const statuses: OrderStatus[] = [ 'delivered', 'delivered', 'completed', 'completed', 'shipped', 'ready', 'confirmed', 'processing', 'draft', 'cancelled', ]; const orders: Order[] = []; const payments: Payment[] = []; const shipments: Shipment[] = []; for (let i = 0; i < 28; i++) { const status = statuses[i % statuses.length]; const contact = contacts[i % contacts.length]; const warehouse = warehouses[i % warehouses.length]; const orderNumber = `ORD-2026-${String(1001 + i).padStart(4, '0')}`; const orderId = `ord_seed_${i + 1}`; // Select 2-4 products for order items const itemCount = 2 + (i % 3); const orderItems = []; let subtotalMinor = 0; for (let j = 0; j < itemCount; j++) { const p = products[(i * 3 + j) % products.length]; const qty = 1 + ((i + j) % 4); const totalItemMinor = p.price.minor * qty; subtotalMinor += totalItemMinor; orderItems.push({ id: `oi_seed_${i + 1}_${j + 1}`, productId: p.id, sku: p.sku, name: p.name, quantity: qty, unitPrice: p.price, discountMinor: 0, totalPrice: { minor: totalItemMinor, currency: 'RUB' }, }); } const discountMinor = i % 5 === 0 ? Math.round(subtotalMinor * 0.05) : 0; const totalMinor = subtotalMinor - discountMinor; const isPaid = status === 'delivered' || status === 'completed' || status === 'shipped' || i % 3 === 0; const isShipped = status === 'delivered' || status === 'completed' || status === 'shipped'; const payStatus: PaymentStatus = isPaid ? 'paid' : status === 'draft' ? 'pending' : 'pending'; const fulfillStatus: FulfillmentStatus = isShipped ? 'fulfilled' : status === 'ready' ? 'partially_fulfilled' : 'unfulfilled'; const order: Order = { id: orderId, orderNumber, customerType: 'contact', customerId: contact.id, customerName: `${contact.firstName} ${contact.lastName}`, status, paymentStatus: payStatus, fulfillmentStatus: fulfillStatus, items: orderItems, subtotal: { minor: subtotalMinor, currency: 'RUB' }, discountMinor, taxMinor: Math.round(totalMinor * 0.2), // 20% VAT included shippingMinor: 0, total: { minor: totalMinor, currency: 'RUB' }, warehouseId: warehouse.id, channel: i % 4 === 0 ? 'pos' : i % 3 === 0 ? 'web' : 'b2b', shippingAddress: `г. Москва, ул. Клиентская, д. ${i + 1}`, audit: { createdAt: relDate(-18 + i * 0.6), createdById: 'usr_sys_01', updatedAt: relDate(-1), updatedById: 'usr_sys_01', version: 1, }, }; orders.push(order); // Linked Payment if (isPaid) { payments.push({ id: `pmt_seed_${i + 1}`, orderId, orderNumber, amount: { minor: totalMinor, currency: 'RUB' }, method: i % 2 === 0 ? 'bank_transfer' : 'card', status: 'completed', transactionId: `TXN-${100000 + i * 17}`, paidAt: relDate(-17 + i * 0.6), audit: { createdAt: relDate(-17 + i * 0.6), createdById: 'usr_sys_01', updatedAt: relDate(-17 + i * 0.6), updatedById: 'usr_sys_01', version: 1, }, }); } // Linked Shipment if (isShipped) { shipments.push({ id: `shp_seed_${i + 1}`, orderId, orderNumber, warehouseId: warehouse.id, status: status === 'delivered' || status === 'completed' ? 'delivered' : 'shipped', trackingNumber: `KRONA-TRK-${70000 + i * 11}`, carrier: 'Krona Экспресс Доставка', dispatchedAt: relDate(-15 + i * 0.6), deliveredAt: status === 'delivered' || status === 'completed' ? relDate(-14 + i * 0.6) : undefined, items: orderItems.map((oi) => ({ productId: oi.productId, sku: oi.sku, name: oi.name, quantity: oi.quantity, })), audit: { createdAt: relDate(-15 + i * 0.6), createdById: 'usr_sys_01', updatedAt: relDate(-1), updatedById: 'usr_sys_01', version: 1, }, }); } } return { orders, payments, shipments }; } // ---------------------------------------------------- // 7. STOCK LEDGER & MOVEMENTS // ---------------------------------------------------- export function generateSeedStock(products: Product[], warehouses: Warehouse[]): { stockItems: StockItem[]; movements: StockMovement[]; } { const stockItems: StockItem[] = []; const movements: StockMovement[] = []; warehouses.forEach((wh) => { products.filter((p) => !p.isService).forEach((p, pIdx) => { const onHand = 30 + ((pIdx * 7) % 150); const reserved = (pIdx % 5) * 3; const available = onHand - reserved; stockItems.push({ id: `stk_${wh.id}_${p.id}`, warehouseId: wh.id, warehouseName: wh.name, productId: p.id, sku: p.sku, productName: p.name, onHand, reserved, available, unit: p.unit, minThreshold: p.minStockAlert, audit: { createdAt: relDate(-40), createdById: 'usr_sys_01', updatedAt: relDate(-2), updatedById: 'usr_sys_01', version: 1, }, }); // Add a couple recent movements if (pIdx % 3 === 0) { movements.push({ id: `mov_${wh.id}_${p.id}_1`, type: 'receipt', productId: p.id, productName: p.name, sku: p.sku, targetWarehouseId: wh.id, quantity: 50, referenceNumber: `GR-${1000 + pIdx}`, notes: 'Плановое поступление от поставщика', timestamp: relDate(-10 + (pIdx % 5)), audit: { createdAt: relDate(-10 + (pIdx % 5)), createdById: 'usr_sys_01', updatedAt: relDate(-10 + (pIdx % 5)), updatedById: 'usr_sys_01', version: 1, }, }); } }); }); return { stockItems, movements }; } // ---------------------------------------------------- // 8. FINANCE ACCOUNTS & CASH-FLOW // ---------------------------------------------------- export function generateSeedFinance(payments: Payment[]): { accounts: Account[]; transactions: CashFlowTransaction[]; } { const accounts: Account[] = [ { id: 'acc_main_bank', name: 'Основной расчетный счет (ПАО Сбербанк)', type: 'bank', balance: { minor: 458000000, currency: 'RUB' }, // 4,580,000.00 RUB currency: 'RUB', accountNumber: '40702810938000012345', bankName: 'ПАО СБЕРБАНК', isDefault: true, status: 'active', audit: { createdAt: relDate(-100), createdById: 'usr_sys_01', updatedAt: relDate(-1), updatedById: 'usr_sys_01', version: 1, }, }, { id: 'acc_cash_desk', name: 'Касса наличных (Главный офис)', type: 'cash_desk', balance: { minor: 34500000, currency: 'RUB' }, // 345,000.00 RUB currency: 'RUB', isDefault: false, status: 'active', audit: { createdAt: relDate(-100), createdById: 'usr_sys_01', updatedAt: relDate(-1), updatedById: 'usr_sys_01', version: 1, }, }, { id: 'acc_acquiring', name: 'Эквайринг Т-Банк', type: 'acquiring', balance: { minor: 189000000, currency: 'RUB' }, // 1,890,000.00 RUB currency: 'RUB', isDefault: false, status: 'active', audit: { createdAt: relDate(-100), createdById: 'usr_sys_01', updatedAt: relDate(-1), updatedById: 'usr_sys_01', version: 1, }, }, ]; const transactions: CashFlowTransaction[] = []; // Inflow transactions matching order payments payments.forEach((pmt, i) => { transactions.push({ id: `tx_seed_${i + 1}`, transactionNumber: `CFT-2026-${String(1001 + i).padStart(4, '0')}`, type: 'inflow', category: 'sales_revenue', amount: pmt.amount, accountId: pmt.method === 'cash' ? 'acc_cash_desk' : 'acc_main_bank', accountName: pmt.method === 'cash' ? 'Касса наличных' : 'Основной расчетный счет', referenceType: 'order_payment', referenceId: pmt.id, description: `Поступление оплаты по заказу №${pmt.orderNumber}`, timestamp: pmt.paidAt, audit: { createdAt: pmt.paidAt, createdById: 'usr_sys_01', updatedAt: pmt.paidAt, updatedById: 'usr_sys_01', version: 1, }, }); }); // A few regular operating outflows (rent, payroll, supplier payments) const outflows = [ { cat: 'rent' as const, name: 'Аренда помещений склада и офиса за текущий месяц', amt: 38000000, daysAgo: 10 }, { cat: 'payroll' as const, name: 'Выплата аванса сотрудникам', amt: 65000000, daysAgo: 15 }, { cat: 'supplier_payment' as const, name: 'Оплата партии сырья поставщику ООО «Кофе Эксперт»', amt: 48500000, daysAgo: 7 }, { cat: 'taxes' as const, name: 'Уплата квартальных налоговых платежей (УСН/НДС)', amt: 29000000, daysAgo: 12 }, { cat: 'utilities' as const, name: 'Оплата электроэнергии и коммунальных услуг', amt: 8500000, daysAgo: 4 }, ]; outflows.forEach((o, idx) => { const timestamp = relDate(-o.daysAgo); transactions.push({ id: `tx_outflow_${idx + 1}`, transactionNumber: `CFT-OUT-${2001 + idx}`, type: 'outflow', category: o.cat, amount: { minor: o.amt, currency: 'RUB' }, accountId: 'acc_main_bank', accountName: 'Основной расчетный счет', referenceType: 'manual', referenceId: `ref_outflow_${idx + 1}`, description: o.name, timestamp, audit: { createdAt: timestamp, createdById: 'usr_sys_01', updatedAt: timestamp, updatedById: 'usr_sys_01', version: 1, }, }); }); return { accounts, transactions }; } // ---------------------------------------------------- // 9. AUDIT LOG INITIAL SEED // ---------------------------------------------------- export function generateSeedAuditLogs(): AuditEntry[] { return [ { id: `aud_seed_1`, entityType: 'system', entityId: 'seed_init', action: 'create', userId: 'usr_sys_01', userName: 'Системный администратор', timestamp: relDate(-30), summary: 'Инициализация демонстрационного контура Krona Universal Business OS', }, { id: `aud_seed_2`, entityType: 'warehouse', entityId: 'wh_main', action: 'create', userId: 'usr_sys_01', userName: 'Администратор склада', timestamp: relDate(-25), summary: 'Создан центральный склад «Москва-Юг» с адресным хранением', }, { id: `aud_seed_3`, entityType: 'company', entityId: 'comp_seed_1', action: 'create', userId: 'usr_sales_01', userName: 'Менеджер по продажам', timestamp: relDate(-20), summary: 'Зарегистрирован ключевой корпоративный клиент ООО «СеверТорг»', }, ]; } // ---------------------------------------------------- // SEEDING AND RESET // ---------------------------------------------------- export async function seedInitialData(force = false): Promise { const count = await countRecords('contact'); if (count > 0 && !force) { return false; } if (force) { await resetDatabase(); } const companies = generateSeedCompanies(); const contacts = generateSeedContacts(companies); const categories = generateSeedCategories(); const products = generateSeedProducts(categories); const warehouses = generateSeedWarehouses(); const deals = generateSeedDeals(contacts, companies); const { orders, payments, shipments } = generateSeedOrders(contacts, products, warehouses); const { stockItems, movements } = generateSeedStock(products, warehouses); const { accounts, transactions } = generateSeedFinance(payments); const auditLogs = generateSeedAuditLogs(); // Save into unified records_data store with entityType for (const item of companies) await putRecord({ ...item, entityType: 'company' }); for (const item of contacts) await putRecord({ ...item, entityType: 'contact' }); for (const item of categories) await putRecord({ ...item, entityType: 'product_category' }); for (const item of products) await putRecord({ ...item, entityType: 'product' }); for (const item of warehouses) await putRecord({ ...item, entityType: 'warehouse' }); for (const item of deals) await putRecord({ ...item, entityType: 'deal' }); for (const item of orders) await putRecord({ ...item, entityType: 'order' }); for (const item of payments) await putRecord({ ...item, entityType: 'payment' }); for (const item of shipments) await putRecord({ ...item, entityType: 'shipment' }); for (const item of stockItems) await putRecord({ ...item, entityType: 'stock_item' }); for (const item of movements) await putRecord({ ...item, entityType: 'stock_movement' }); for (const item of accounts) await putRecord({ ...item, entityType: 'account' }); for (const item of transactions) await putRecord({ ...item, entityType: 'cash_flow_transaction' }); // Save audit logs for (const item of auditLogs) await putStoreItem('audit_log', item); // Save metadata for (const [, meta] of Object.entries(SYSTEM_ENTITY_METADATA)) { await putStoreItem('entities_meta', meta); for (const field of meta.fields) { await putStoreItem('fields_meta', { ...field, entityCode: meta.code }); } } return true; } export async function resetToInitialSeed(): Promise { await seedInitialData(true); }