#!/usr/bin/env node /** * Comprehensive Test Data Generator * * Creates realistic test data for the panel application including: * - Users with different roles * - Contracts with realistic data * - Projects scattered across Poland with person/company names * - Task templates and sets * - Project tasks with various statuses * - Contacts * - Notes and file attachments * - Notifications and audit logs */ import db from '../src/lib/db.js'; import initializeDatabase from '../src/lib/init-db.js'; import bcrypt from 'bcryptjs'; import crypto from 'crypto'; // Configuration const CONFIG = { clearExistingData: true, preserveAdmin: true, // Keep existing admin user seed: 42, // For reproducible random data }; // Seeded random number generator class SeededRandom { constructor(seed) { this.seed = seed; } next() { this.seed = (this.seed * 9301 + 49297) % 233280; return this.seed / 233280; } choice(array) { return array[Math.floor(this.next() * array.length)]; } integer(min, max) { return Math.floor(this.next() * (max - min + 1)) + min; } boolean(probability = 0.5) { return this.next() < probability; } } const random = new SeededRandom(CONFIG.seed); // Polish cities with coordinates const POLISH_CITIES = [ { name: 'Warszawa', coordinates: '52.2297,21.0122' }, { name: 'Kraków', coordinates: '50.0647,19.9450' }, { name: 'Wrocław', coordinates: '51.1079,17.0385' }, { name: 'Poznań', coordinates: '52.4064,16.9252' }, { name: 'Gdańsk', coordinates: '54.3520,18.6466' }, { name: 'Szczecin', coordinates: '53.4289,14.5530' }, { name: 'Lublin', coordinates: '51.2465,22.5684' }, { name: 'Katowice', coordinates: '50.2649,19.0238' }, { name: 'Łódź', coordinates: '51.7592,19.4600' }, { name: 'Bydgoszcz', coordinates: '53.1235,18.0084' }, { name: 'Białystok', coordinates: '53.1325,23.1688' }, { name: 'Rzeszów', coordinates: '50.0412,21.9991' }, ]; // Street names const STREET_TYPES = ['ul.', 'al.', 'pl.']; const STREET_NAMES = [ 'Główna', 'Kwiatowa', 'Słoneczna', 'Przemysłowa', 'Leśna', 'Parkowa', 'Centralna', 'Sportowa', 'Polna', 'Krótka', 'Długa', 'Nowa', 'Stara', 'Morska', 'Górska', 'Wolności', 'Mickiewicza', 'Kościuszki', 'Piłsudskiego', 'Kolejowa' ]; // Project names - people const PERSON_NAMES = [ 'Jan Kowalski', 'Anna Nowak', 'Piotr Wiśniewski', 'Maria Lewandowska', 'Tomasz Kamiński', 'Małgorzata Zielińska', 'Krzysztof Szymański', 'Agnieszka Woźniak', 'Andrzej Dąbrowski', 'Barbara Kozłowska', 'Józef Jankowski', 'Ewa Wojciechowska', 'Stanisław Kwiatkowski', 'Krystyna Kaczmarek', 'Tadeusz Piotrowski' ]; // Project names - companies const COMPANY_NAMES = [ 'PolBud Sp. z o.o.', 'Constructo Group', 'BuildMaster SA', 'EuroDevelopment', 'Invest Property', 'Metropolitan Construction', 'Green Building Solutions', 'Nova Inwestycje', 'Prime Estate', 'TechBuild Industries', 'Horizon Development', 'Skyline Properties', 'Urban Solutions', 'Future Living', 'Capital Investments' ]; // Task templates const DESIGN_TASKS = [ { name: 'Wstępne uzgodnienia z klientem', max_wait_days: 7 }, { name: 'Wizja lokalna i pomiary', max_wait_days: 5 }, { name: 'Projekt koncepcyjny', max_wait_days: 14 }, { name: 'Uzgodnienia projektu koncepcyjnego', max_wait_days: 7 }, { name: 'Projekt budowlany', max_wait_days: 21 }, { name: 'Projekt wykonawczy', max_wait_days: 21 }, { name: 'Specyfikacja techniczna', max_wait_days: 10 }, { name: 'Kosztorys inwestorski', max_wait_days: 7 }, { name: 'Wniosek o pozwolenie na budowę', max_wait_days: 14 }, { name: 'Uzyskanie pozwolenia na budowę', max_wait_days: 60 }, { name: 'Projekt wykonawczy - instalacje', max_wait_days: 21 }, { name: 'Projekt zagospodarowania terenu', max_wait_days: 14 }, { name: 'Dokumentacja powykonawcza', max_wait_days: 14 }, ]; const CONSTRUCTION_TASKS = [ { name: 'Przygotowanie placu budowy', max_wait_days: 7 }, { name: 'Wykopy i fundamenty', max_wait_days: 14 }, { name: 'Stan zero', max_wait_days: 21 }, { name: 'Stan surowy otwarty', max_wait_days: 30 }, { name: 'Stan surowy zamknięty', max_wait_days: 30 }, { name: 'Instalacje wewnętrzne', max_wait_days: 21 }, { name: 'Tynki i wylewki', max_wait_days: 14 }, { name: 'Stolarka okienna i drzwiowa', max_wait_days: 10 }, { name: 'Wykończenie - malowanie', max_wait_days: 14 }, { name: 'Wykończenie - podłogi', max_wait_days: 10 }, { name: 'Instalacje sanitarne', max_wait_days: 14 }, { name: 'Instalacje elektryczne', max_wait_days: 14 }, { name: 'Odbiór techniczny', max_wait_days: 7 }, { name: 'Odbiór końcowy', max_wait_days: 7 }, { name: 'Przekazanie dokumentacji', max_wait_days: 5 }, ]; // Contact types and data const CONTACT_FIRST_NAMES = ['Jan', 'Piotr', 'Anna', 'Maria', 'Tomasz', 'Krzysztof', 'Agnieszka', 'Magdalena', 'Andrzej', 'Ewa']; const CONTACT_LAST_NAMES = ['Kowalski', 'Nowak', 'Wiśniewski', 'Lewandowski', 'Kamiński', 'Zieliński', 'Szymański', 'Woźniak', 'Dąbrowski', 'Kozłowski']; const POSITIONS = ['Kierownik projektu', 'Inżynier', 'Architekt', 'Inspektor nadzoru', 'Przedstawiciel inwestora', 'Dyrektor', 'Koordynator']; // Helper functions function generateId() { return crypto.randomBytes(16).toString('hex'); } function generateWP() { const part1 = String(random.integer(100000, 999999)); const part2 = String(random.integer(1000, 9999)); const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const part3 = Array(6).fill(0).map(() => chars[random.integer(0, chars.length - 1)]).join(''); return `${part1}/${part2}/${part3}`; } function generateInvestmentNumber() { const letter = String.fromCharCode(65 + random.integer(0, 25)); // A-Z const letters = String.fromCharCode(65 + random.integer(0, 25)) + String.fromCharCode(65 + random.integer(0, 25)); const number = String(random.integer(1000000, 9999999)); return `${letter}-${letters}-${number}`; } function generateDate(startDate, endDate) { const start = new Date(startDate).getTime(); const end = new Date(endDate).getTime(); const timestamp = start + random.next() * (end - start); return new Date(timestamp).toISOString().split('T')[0]; } function addDays(dateStr, days) { const date = new Date(dateStr); date.setDate(date.getDate() + days); return date.toISOString().split('T')[0]; } function generatePhoneNumber() { return `${random.integer(500, 799)}-${random.integer(100, 999)}-${random.integer(100, 999)}`; } // Clear existing data function clearData() { console.log('\n🗑️ Clearing existing data...\n'); const tables = [ 'field_change_history', 'notifications', 'audit_logs', 'file_attachments', 'notes', 'project_tasks', 'task_set_templates', 'task_sets', 'tasks', 'project_contacts', 'contacts', 'projects', 'contracts', 'password_reset_tokens', 'sessions', ]; if (!CONFIG.preserveAdmin) { tables.push('users'); } tables.forEach(table => { try { db.prepare(`DELETE FROM ${table}`).run(); console.log(` ✓ Cleared ${table}`); } catch (error) { console.log(` ⚠ Warning clearing ${table}:`, error.message); } }); // Reset sequences db.prepare('DELETE FROM sqlite_sequence').run(); console.log('\n✅ Data cleared successfully\n'); } // Phase 1: Create Users function createUsers() { console.log('\n👥 Creating users...\n'); const users = []; const defaultPassword = bcrypt.hashSync('password123', 10); // Keep existing admin if preserveAdmin is true if (CONFIG.preserveAdmin) { const existingAdmin = db.prepare('SELECT * FROM users WHERE role = ?').get('admin'); if (existingAdmin) { users.push(existingAdmin); console.log(` ✓ Preserved existing admin: ${existingAdmin.username}`); } } const newUsers = [ { name: 'Maria Kowalska', username: 'maria.kowalska', role: 'team_lead' }, { name: 'Piotr Nowak', username: 'piotr.nowak', role: 'team_lead' }, { name: 'Anna Wiśniewska', username: 'anna.wisniewska', role: 'project_manager' }, { name: 'Tomasz Kamiński', username: 'tomasz.kaminski', role: 'project_manager' }, { name: 'Krzysztof Lewandowski', username: 'krzysztof.lewandowski', role: 'project_manager' }, { name: 'Agnieszka Zielińska', username: 'agnieszka.zielinska', role: 'user' }, { name: 'Marek Szymański', username: 'marek.szymanski', role: 'user' }, { name: 'Ewa Dąbrowska', username: 'ewa.dabrowska', role: 'user' }, { name: 'Janusz Kozłowski', username: 'janusz.kozlowski', role: 'user' }, { name: 'Barbara Wojciechowska', username: 'barbara.wojciechowska', role: 'user' }, { name: 'Viewing Account', username: 'viewer', role: 'read_only' }, ]; newUsers.forEach(userData => { const userId = generateId(); // Generate initials from name const nameParts = userData.name.trim().split(/\s+/); const initial = nameParts.map(part => part.charAt(0).toUpperCase()).join(''); db.prepare(` INSERT INTO users (id, name, username, password_hash, role, initial, is_active, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) `).run(userId, userData.name, userData.username, defaultPassword, userData.role, initial); users.push({ id: userId, ...userData }); console.log(` ✓ Created ${userData.role}: ${userData.name} (${userData.username})`); }); console.log(`\n✅ Created ${newUsers.length} new users (Total: ${users.length})\n`); return users; } // Phase 2: Create Contracts function createContracts() { console.log('\n📄 Creating contracts...\n'); const contracts = [ { number: '2025/FW-001', name: 'Umowa ramowa - projekty mieszkaniowe 2025', customer: 'Deweloper Mieszkaniowy Sp. z o.o.', investor: 'Invest Property Fund', customerContractNumber: 'DMH/2025/001', dateSigned: '2025-01-10', finishDate: '2026-12-31', }, { number: '2025/INF-002', name: 'Projekty infrastrukturalne miasta', customer: 'Zarząd Dróg Miejskich', investor: 'Gmina Miasto', customerContractNumber: 'ZDM-2025-02-INF', dateSigned: '2025-02-01', finishDate: '2026-06-30', }, { number: '2025/COM-003', name: 'Obiekty komercyjne - centra handlowe', customer: 'Retail Development Group', investor: 'Metropolitan Investments', customerContractNumber: 'RDG/25/COM/03', dateSigned: '2025-01-15', finishDate: '2026-09-30', }, ]; const contractIds = []; contracts.forEach((contract, index) => { const result = db.prepare(` INSERT INTO contracts ( contract_number, contract_name, customer_contract_number, customer, investor, date_signed, finish_date ) VALUES (?, ?, ?, ?, ?, ?, ?) `).run( contract.number, contract.name, contract.customerContractNumber, contract.customer, contract.investor, contract.dateSigned, contract.finishDate ); contractIds.push(result.lastInsertRowid); console.log(` ✓ Created contract: ${contract.number} - ${contract.name}`); }); console.log(`\n✅ Created ${contracts.length} contracts\n`); return contractIds; } // Phase 3: Create Projects function createProjects(contractIds, users) { console.log('\n🏗️ Creating projects...\n'); const projectCount = random.integer(12, 15); const projects = []; const projectStatuses = ['registered', 'in_progress_design', 'in_progress_construction', 'fulfilled', 'cancelled']; const projectTypes = ['design', 'construction', 'design+construction']; const usedCities = []; for (let i = 0; i < projectCount; i++) { // Select contract const contractId = random.choice(contractIds); const contractInfo = db.prepare('SELECT contract_number FROM contracts WHERE contract_id = ?').get(contractId); // Get sequential number for this contract const existingCount = db.prepare('SELECT COUNT(*) as count FROM projects WHERE contract_id = ?').get(contractId); const sequenceNumber = existingCount.count + 1; const projectNumber = `${sequenceNumber}/${contractInfo.contract_number}`; // Select city (try to use different cities) let city; if (usedCities.length < POLISH_CITIES.length) { const availableCities = POLISH_CITIES.filter(c => !usedCities.includes(c.name)); city = random.choice(availableCities); usedCities.push(city.name); } else { city = random.choice(POLISH_CITIES); } // Generate address const streetType = random.choice(STREET_TYPES); const streetName = random.choice(STREET_NAMES); const buildingNumber = random.integer(1, 200); const address = `${streetType} ${streetName} ${buildingNumber}`; // Project name (person or company) const projectName = random.boolean(0.6) ? random.choice(PERSON_NAMES) : random.choice(COMPANY_NAMES); // Project type and status const projectType = random.choice(projectTypes); const projectStatus = random.choice(projectStatuses); // Dates const startDate = generateDate('2025-01-01', '2025-12-31'); const finishDate = addDays(startDate, random.integer(90, 365)); const completionDate = (projectStatus === 'fulfilled') ? addDays(finishDate, random.integer(-30, 10)) : null; // Other fields const wp = generateWP(); const investmentNumber = generateInvestmentNumber(); const plot = `${random.integer(1, 500)}/${random.integer(1, 50)}`; const district = random.choice(['Centrum', 'Północ', 'Południe', 'Wschód', 'Zachód', 'Śródmieście']); const unit = random.choice(['A', 'B', 'C', 'D', 'E', '1', '2', '3']); // Assign to project manager const projectManagers = users.filter(u => u.role === 'project_manager'); const assignedTo = random.choice(projectManagers).id; const createdBy = random.choice(users.filter(u => u.role === 'admin' || u.role === 'team_lead')).id; const wartoscZlecenia = random.integer(100000, 5000000); const result = db.prepare(` INSERT INTO projects ( contract_id, project_name, project_number, address, plot, district, unit, city, investment_number, start_date, finish_date, completion_date, wp, coordinates, project_type, project_status, wartosc_zlecenia, created_by, assigned_to, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) `).run( contractId, projectName, projectNumber, address, plot, district, unit, city.name, investmentNumber, startDate, finishDate, completionDate, wp, city.coordinates, projectType, projectStatus, wartoscZlecenia, createdBy, assignedTo ); projects.push({ id: result.lastInsertRowid, name: projectName, number: projectNumber, type: projectType, status: projectStatus, city: city.name, assignedTo: assignedTo, createdBy: createdBy, startDate: startDate, }); console.log(` ✓ ${projectNumber}: ${projectName} (${city.name}) - ${projectStatus}`); } console.log(`\n✅ Created ${projects.length} projects\n`); return projects; } // Phase 4: Create Task Templates function createTaskTemplates() { console.log('\n✅ Creating task templates...\n'); const taskIds = { design: [], construction: [] }; console.log(' Design tasks:'); DESIGN_TASKS.forEach(task => { const result = db.prepare(` INSERT INTO tasks (name, max_wait_days, is_standard, task_category) VALUES (?, ?, 1, 'design') `).run(task.name, task.max_wait_days); taskIds.design.push(result.lastInsertRowid); console.log(` ✓ ${task.name}`); }); console.log('\n Construction tasks:'); CONSTRUCTION_TASKS.forEach(task => { const result = db.prepare(` INSERT INTO tasks (name, max_wait_days, is_standard, task_category) VALUES (?, ?, 1, 'construction') `).run(task.name, task.max_wait_days); taskIds.construction.push(result.lastInsertRowid); console.log(` ✓ ${task.name}`); }); console.log(`\n✅ Created ${DESIGN_TASKS.length + CONSTRUCTION_TASKS.length} task templates\n`); return taskIds; } // Phase 5: Create Task Sets function createTaskSets(taskIds) { console.log('\n📋 Creating task sets...\n'); const sets = [ { name: 'Standard - Projektowanie', category: 'design', tasks: taskIds.design.slice(0, 8), }, { name: 'Pełny zakres - Projektowanie', category: 'design', tasks: taskIds.design, }, { name: 'Standard - Budowa', category: 'construction', tasks: taskIds.construction.slice(0, 10), }, { name: 'Pełny zakres - Budowa', category: 'construction', tasks: taskIds.construction, }, ]; const setIds = []; sets.forEach(set => { const result = db.prepare(` INSERT INTO task_sets (name, task_category, created_at, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) `).run(set.name, set.category); const setId = result.lastInsertRowid; setIds.push(setId); // Add tasks to set set.tasks.forEach((taskId, index) => { db.prepare(` INSERT INTO task_set_templates (set_id, task_template_id, sort_order) VALUES (?, ?, ?) `).run(setId, taskId, index); }); console.log(` ✓ ${set.name} (${set.tasks.length} tasks)`); }); console.log(`\n✅ Created ${sets.length} task sets\n`); return setIds; } // Phase 6: Create Project Tasks function createProjectTasks(projects, taskIds, users) { console.log('\n📝 Creating project tasks...\n'); const taskStatuses = ['not_started', 'in_progress', 'completed', 'cancelled']; const priorities = ['normal', 'low', 'high']; let totalTasks = 0; projects.forEach(project => { // Select appropriate tasks based on project type let availableTasks = []; if (project.type === 'design') { availableTasks = taskIds.design; } else if (project.type === 'construction') { availableTasks = taskIds.construction; } else { availableTasks = [...taskIds.design, ...taskIds.construction]; } // Create 3-7 tasks per project const taskCount = random.integer(3, 7); const selectedTasks = []; // Select random tasks for (let i = 0; i < taskCount && selectedTasks.length < availableTasks.length; i++) { let taskId; do { taskId = random.choice(availableTasks); } while (selectedTasks.includes(taskId)); selectedTasks.push(taskId); } selectedTasks.forEach(taskTemplateId => { // Determine status based on project status let status; if (project.status === 'registered') { status = 'not_started'; } else if (project.status === 'fulfilled') { status = 'completed'; } else if (project.status === 'cancelled') { status = random.choice(['not_started', 'cancelled']); } else { status = random.choice(taskStatuses.slice(0, 3)); // not_started, in_progress, completed } const priority = random.choice(priorities); // Dates let dateAdded = project.startDate; let dateStarted = null; let dateCompleted = null; if (status === 'in_progress' || status === 'completed') { dateStarted = addDays(dateAdded, random.integer(1, 30)); } if (status === 'completed') { dateCompleted = addDays(dateStarted, random.integer(5, 60)); } // Assignment const regularUsers = users.filter(u => u.role === 'user' || u.role === 'project_manager'); const assignedTo = random.boolean(0.7) ? random.choice(regularUsers).id : null; db.prepare(` INSERT INTO project_tasks ( project_id, task_template_id, status, priority, date_added, date_started, date_completed, created_by, assigned_to, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) `).run( project.id, taskTemplateId, status, priority, dateAdded, dateStarted, dateCompleted, project.createdBy, assignedTo ); totalTasks++; }); console.log(` ✓ ${project.number}: Created ${taskCount} tasks`); }); console.log(`\n✅ Created ${totalTasks} project tasks\n`); } // Phase 7: Create Contacts function createContacts(users) { console.log('\n👤 Creating contacts...\n'); const contactTypes = ['project', 'contractor', 'office', 'supplier', 'other']; const contacts = []; const contactCount = random.integer(25, 35); for (let i = 0; i < contactCount; i++) { const firstName = random.choice(CONTACT_FIRST_NAMES); const lastName = random.choice(CONTACT_LAST_NAMES); const name = `${firstName} ${lastName}`; const phone = generatePhoneNumber(); const email = random.boolean(0.6) ? `${firstName.toLowerCase()}.${lastName.toLowerCase()}@example.com` : null; const company = random.boolean(0.5) ? random.choice(COMPANY_NAMES) : null; const position = random.boolean(0.7) ? random.choice(POSITIONS) : null; const contactType = random.choice(contactTypes); const result = db.prepare(` INSERT INTO contacts ( name, phone, email, company, position, contact_type, is_active, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) `).run(name, phone, email, company, position, contactType); contacts.push({ id: result.lastInsertRowid, name: name, type: contactType, }); } console.log(` ✓ Created ${contacts.length} contacts\n`); return contacts; } // Phase 8: Link Projects to Contacts function linkProjectContacts(projects, contacts, users) { console.log('\n🔗 Linking projects to contacts...\n'); let linkCount = 0; projects.forEach(project => { // Link 1-4 contacts per project const contactsToLink = random.integer(1, 4); const linkedContacts = []; for (let i = 0; i < contactsToLink; i++) { let contact; do { contact = random.choice(contacts); } while (linkedContacts.includes(contact.id)); linkedContacts.push(contact.id); const isPrimary = i === 0 ? 1 : 0; const relationshipType = random.choice(['general', 'technical', 'commercial', 'administrative']); const addedBy = random.choice(users).id; try { db.prepare(` INSERT INTO project_contacts ( project_id, contact_id, relationship_type, is_primary, added_by, added_at ) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) `).run(project.id, contact.id, relationshipType, isPrimary, addedBy); linkCount++; } catch (error) { // Ignore duplicate key errors } } }); console.log(` ✓ Created ${linkCount} project-contact links\n`); } // Phase 9: Create Notes function createNotes(projects, users) { console.log('\n📝 Creating notes...\n'); const noteTemplates = [ 'Spotkanie z klientem - uzgodniono zakres prac', 'Wykonano wizję lokalną', 'Przesłano dokumentację do uzgodnień', 'Otrzymano uwagi do projektu', 'Zaktualizowano dokumentację zgodnie z uwagami', 'Projekt zatwierdzony przez inwestora', 'Rozpoczęto prace na budowie', 'Wykonano odbiór częściowy', 'Zgłoszono problemy techniczne', 'Problem rozwiązany', 'Zamówiono materiały', 'Dostawa materiałów opóźniona', 'Materiały dostarczone na plac budowy', ]; let noteCount = 0; projects.forEach(project => { // Create 2-6 notes per project const notesPerProject = random.integer(2, 6); for (let i = 0; i < notesPerProject; i++) { const note = random.choice(noteTemplates); const createdBy = random.choice(users).id; const isSystem = random.boolean(0.1) ? 1 : 0; // Generate date between project start and now const noteDate = generateDate(project.startDate, '2026-01-26'); db.prepare(` INSERT INTO notes ( project_id, note, note_date, is_system, created_by ) VALUES (?, ?, ?, ?, ?) `).run(project.id, note, noteDate, isSystem, createdBy); noteCount++; } }); console.log(` ✓ Created ${noteCount} notes\n`); } // Phase 10: Create Audit Logs function createAuditLogs(users, projects) { console.log('\n📊 Creating audit logs...\n'); const actions = [ 'user.login', 'project.create', 'project.update', 'project.view', 'task.create', 'task.update', 'task.complete', 'file.upload', 'contract.create', 'contact.create', ]; const ipAddresses = [ '192.168.1.100', '192.168.1.101', '10.0.0.50', '172.16.0.10', '83.24.156.78', ]; let logCount = 0; // Create 100-200 audit logs const totalLogs = random.integer(100, 200); for (let i = 0; i < totalLogs; i++) { const user = random.choice(users); const action = random.choice(actions); const timestamp = generateDate('2025-01-01', '2026-01-26'); const ip = random.choice(ipAddresses); let resourceType = null; let resourceId = null; if (action.includes('project')) { resourceType = 'project'; resourceId = String(random.choice(projects).id); } db.prepare(` INSERT INTO audit_logs ( user_id, action, resource_type, resource_id, ip_address, user_agent, timestamp ) VALUES (?, ?, ?, ?, ?, ?, ?) `).run( user.id, action, resourceType, resourceId, ip, 'Mozilla/5.0 (compatible)', timestamp ); logCount++; } console.log(` ✓ Created ${logCount} audit logs\n`); } // Main execution async function main() { console.log('\n╔════════════════════════════════════════════════════════╗'); console.log('║ Comprehensive Test Data Generator ║'); console.log('╚════════════════════════════════════════════════════════╝'); try { // Initialize database console.log('\n🔧 Initializing database schema...'); initializeDatabase(); console.log('✅ Database schema ready\n'); // Clear existing data if (CONFIG.clearExistingData) { clearData(); } // Generate data in phases const users = createUsers(); const contractIds = createContracts(); const projects = createProjects(contractIds, users); const taskIds = createTaskTemplates(); const taskSetIds = createTaskSets(taskIds); createProjectTasks(projects, taskIds, users); const contacts = createContacts(users); linkProjectContacts(projects, contacts, users); createNotes(projects, users); createAuditLogs(users, projects); // Summary console.log('\n╔════════════════════════════════════════════════════════╗'); console.log('║ SUMMARY ║'); console.log('╚════════════════════════════════════════════════════════╝\n'); const stats = { users: db.prepare('SELECT COUNT(*) as count FROM users').get().count, contracts: db.prepare('SELECT COUNT(*) as count FROM contracts').get().count, projects: db.prepare('SELECT COUNT(*) as count FROM projects').get().count, tasks: db.prepare('SELECT COUNT(*) as count FROM tasks').get().count, taskSets: db.prepare('SELECT COUNT(*) as count FROM task_sets').get().count, projectTasks: db.prepare('SELECT COUNT(*) as count FROM project_tasks').get().count, contacts: db.prepare('SELECT COUNT(*) as count FROM contacts').get().count, projectContacts: db.prepare('SELECT COUNT(*) as count FROM project_contacts').get().count, notes: db.prepare('SELECT COUNT(*) as count FROM notes').get().count, auditLogs: db.prepare('SELECT COUNT(*) as count FROM audit_logs').get().count, }; console.log(` 👥 Users: ${stats.users}`); console.log(` 📄 Contracts: ${stats.contracts}`); console.log(` 🏗️ Projects: ${stats.projects}`); console.log(` ✅ Task Templates: ${stats.tasks}`); console.log(` 📋 Task Sets: ${stats.taskSets}`); console.log(` 📝 Project Tasks: ${stats.projectTasks}`); console.log(` 👤 Contacts: ${stats.contacts}`); console.log(` 🔗 Project-Contacts: ${stats.projectContacts}`); console.log(` 📝 Notes: ${stats.notes}`); console.log(` 📊 Audit Logs: ${stats.auditLogs}`); console.log('\n✨ Test data generation completed successfully!\n'); console.log('💡 Default password for all users: password123\n'); } catch (error) { console.error('\n❌ Error:', error.message); console.error(error.stack); process.exit(1); } } main();