import 'dotenv/config'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import db from '../src/database/models/index.js'; import { syncLocationManagers, syncRegionManager, syncZoneManager } from '../src/modules/master/syncHierarchy.service.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); async function run() { console.log('--- Seeding Real Geo Data (District -> Area Hierarchy) ---'); try { await db.sequelize.authenticate(); // Read the source seeder file const seederPath = path.join(__dirname, '../seeders/20240127-seed-geo-data.js'); const content = fs.readFileSync(seederPath, 'utf8'); // Extract arrays using regex const zonesMatch = content.match(/const ZONES_DATA = \[([\s\S]*?)\];/); const statesMatch = content.match(/const STATES_DATA = \[([\s\S]*?)\];/); const citiesMatch = content.match(/const CITIES_DATA = \[([\s\S]*?)\];/); if (!zonesMatch || !statesMatch || !citiesMatch) { throw new Error('Could not parse geo data arrays from seeder file!'); } // Eval helper (data is trusted since it's our own seeder) const ZONES_DATA = eval(`[${zonesMatch[1]}]`); const STATES_DATA = eval(`[${statesMatch[1]}]`); const CITIES_DATA = eval(`[${citiesMatch[1]}]`); console.log(`Extracted ${ZONES_DATA.length} Zones, ${STATES_DATA.length} States, and ${CITIES_DATA.length} Cities.`); const { Zone, State, District, Location, Opportunity } = db; // 1. Seed Zones const zoneIdMap = new Map(); // Name -> UUID for (const z of ZONES_DATA) { const [zoneRecord] = await Zone.findOrCreate({ where: { name: z.name }, defaults: { name: z.name, code: z.code } }); zoneIdMap.set(z.name, zoneRecord.id); } console.log('Zones seeded.'); // 2. Seed States and link to Zones const stateIdMap = new Map(); // Legacy ID -> { id, zoneId } for (const s of STATES_DATA) { const parentZoneData = ZONES_DATA.find((z: any) => z.states.includes(s.name)); const zoneId = parentZoneData ? zoneIdMap.get(parentZoneData.name) : null; const [stateRecord] = await State.findOrCreate({ where: { name: s.name }, defaults: { name: s.name, zoneId: zoneId } }); stateIdMap.set(s.id, { id: stateRecord.id, zoneId: zoneId }); } console.log('States seeded.'); // 3. Seed Districts and Areas (Locations) let districtCount = 0; let areaCount = 0; let opportunityCount = 0; for (const c of CITIES_DATA) { const parentStateData = stateIdMap.get(c.state_id); if (parentStateData) { // a. Create District (Primary territory entity) const [districtRecord] = await District.findOrCreate({ where: { name: c.name, stateId: parentStateData.id }, defaults: { name: c.name, stateId: parentStateData.id, zoneId: parentStateData.zoneId } }); districtCount++; // b. Create Area (Granular Location record) const [areaRecord] = await Location.findOrCreate({ where: { name: c.name, districtId: districtRecord.id }, defaults: { name: c.name, city: c.name, districtId: districtRecord.id, isActive: true } }); areaCount++; // c. Create associated Opportunity const [oppRecord, created] = await Opportunity.findOrCreate({ where: { areaId: areaRecord.id }, defaults: { districtId: districtRecord.id, areaId: areaRecord.id, // Linking to Area! city: c.name, status: 'inactive', opportunityType: 'New Dealership', capacity: 'Standard', priority: 'Medium', notes: 'Automatically generated from district seed' } }); if (created) opportunityCount++; } } console.log(`✅ Seeded ${districtCount} Districts, ${areaCount} Areas, and ${opportunityCount} Opportunities.`); console.log('--- Triggering Hierarchy Synchronization ---'); const districts = await District.findAll({ attributes: ['id'] }); for (const d of districts) await syncLocationManagers(d.id); const regions = await db.Region.findAll({ attributes: ['id'] }); for (const r of regions) await syncRegionManager(r.id); const zonesArr = await Zone.findAll({ attributes: ['id'] }); for (const z of zonesArr) await syncZoneManager(z.id); console.log('--- Synchronization Complete ---'); process.exit(0); } catch (e: any) { console.error('❌ Failed:', e.message); process.exit(1); } } run();