mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
256 lines
21 KiB
TypeScript
256 lines
21 KiB
TypeScript
import { PrismaClient, ServiceClass, UserRole, LoyaltyTier, SeatKind, PassengerCategory, Currency } from '@prisma/client';
|
|
import * as bcrypt from 'bcrypt';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
console.log('🌱 Starting comprehensive seed...');
|
|
|
|
// All 21 Stations (Ethiopian-Djibouti Railway)
|
|
const addis = await prisma.station.upsert({ where: { code: 'ADD' }, update: {}, create: { code: 'ADD', name: 'Addis Ababa Central', city: 'Addis Ababa', countryCode: 'ET', lat: 9.0054, lng: 38.7636 } });
|
|
const sebeta = await prisma.station.upsert({ where: { code: 'SBT' }, update: {}, create: { code: 'SBT', name: 'Sebeta', city: 'Sebeta', countryCode: 'ET', lat: 8.9167, lng: 38.6167 } });
|
|
const labu = await prisma.station.upsert({ where: { code: 'LBU' }, update: {}, create: { code: 'LBU', name: 'Labu', city: 'Labu', countryCode: 'ET', lat: 8.8500, lng: 38.8500 } });
|
|
const indode = await prisma.station.upsert({ where: { code: 'IND' }, update: {}, create: { code: 'IND', name: 'Indode', city: 'Indode', countryCode: 'ET', lat: 8.7833, lng: 39.0167 } });
|
|
const bishoftu = await prisma.station.upsert({ where: { code: 'BSH' }, update: {}, create: { code: 'BSH', name: 'Bishoftu', city: 'Bishoftu', countryCode: 'ET', lat: 8.7500, lng: 38.9833 } });
|
|
const mojo = await prisma.station.upsert({ where: { code: 'MJO' }, update: {}, create: { code: 'MJO', name: 'Mojo', city: 'Mojo', countryCode: 'ET', lat: 8.5833, lng: 39.1167 } });
|
|
const adama = await prisma.station.upsert({ where: { code: 'ADM' }, update: {}, create: { code: 'ADM', name: 'Adama', city: 'Adama', countryCode: 'ET', lat: 8.5400, lng: 39.2675 } });
|
|
const feto = await prisma.station.upsert({ where: { code: 'FTO' }, update: {}, create: { code: 'FTO', name: 'Feto', city: 'Feto', countryCode: 'ET', lat: 8.7167, lng: 39.5833 } });
|
|
const metahara = await prisma.station.upsert({ where: { code: 'MTH' }, update: {}, create: { code: 'MTH', name: 'Metahara', city: 'Metahara', countryCode: 'ET', lat: 8.9000, lng: 39.9167 } });
|
|
const awash = await prisma.station.upsert({ where: { code: 'AWS' }, update: {}, create: { code: 'AWS', name: 'Awash', city: 'Awash', countryCode: 'ET', lat: 8.9833, lng: 40.1667 } });
|
|
const mieso = await prisma.station.upsert({ where: { code: 'MSO' }, update: {}, create: { code: 'MSO', name: 'Mieso', city: 'Mieso', countryCode: 'ET', lat: 9.2333, lng: 40.7500 } });
|
|
const bike = await prisma.station.upsert({ where: { code: 'BKE' }, update: {}, create: { code: 'BKE', name: 'Bike', city: 'Bike', countryCode: 'ET', lat: 9.4167, lng: 41.2500 } });
|
|
const direDawa = await prisma.station.upsert({ where: { code: 'DDW' }, update: {}, create: { code: 'DDW', name: 'Dire Dawa', city: 'Dire Dawa', countryCode: 'ET', lat: 9.5931, lng: 41.8661 } });
|
|
const arawa = await prisma.station.upsert({ where: { code: 'ARW' }, update: {}, create: { code: 'ARW', name: 'Arawa', city: 'Arawa', countryCode: 'ET', lat: 10.0833, lng: 42.2500 } });
|
|
const adigala = await prisma.station.upsert({ where: { code: 'ADG' }, update: {}, create: { code: 'ADG', name: 'Adigala', city: 'Adigala', countryCode: 'ET', lat: 10.5000, lng: 42.5833 } });
|
|
const aysha = await prisma.station.upsert({ where: { code: 'AYS' }, update: {}, create: { code: 'AYS', name: 'Aysha', city: 'Aysha', countryCode: 'ET', lat: 11.5500, lng: 42.7167 } });
|
|
const dawanle = await prisma.station.upsert({ where: { code: 'DWN' }, update: {}, create: { code: 'DWN', name: 'Dawanle', city: 'Dawanle', countryCode: 'DJ', timezone: 'Africa/Djibouti', lat: 11.3833, lng: 42.8500 } });
|
|
const alisabieh = await prisma.station.upsert({ where: { code: 'ALI' }, update: {}, create: { code: 'ALI', name: 'Alisabieh', city: 'Alisabieh', countryCode: 'DJ', timezone: 'Africa/Djibouti', lat: 11.1667, lng: 42.7167 } });
|
|
const holhol = await prisma.station.upsert({ where: { code: 'HLH' }, update: {}, create: { code: 'HLH', name: 'Holhol', city: 'Holhol', countryCode: 'DJ', timezone: 'Africa/Djibouti', lat: 11.4167, lng: 43.0000 } });
|
|
const nagad = await prisma.station.upsert({ where: { code: 'NGD' }, update: {}, create: { code: 'NGD', name: 'Nagad', city: 'Nagad', countryCode: 'DJ', timezone: 'Africa/Djibouti', lat: 11.5167, lng: 43.1000 } });
|
|
const djibouti = await prisma.station.upsert({ where: { code: 'DJI' }, update: {}, create: { code: 'DJI', name: 'Djibouti', city: 'Djibouti', countryCode: 'DJ', timezone: 'Africa/Djibouti', lat: 11.5720, lng: 43.1456 } });
|
|
|
|
// Routes
|
|
const route1 = await prisma.route.upsert({
|
|
where: { code: 'R001' },
|
|
update: {},
|
|
create: { code: 'R001', name: 'Addis Ababa - Djibouti Express', effectiveFrom: new Date('2026-01-01'), active: true }
|
|
});
|
|
|
|
// Delete existing route stops and recreate
|
|
await prisma.routeStop.deleteMany({ where: { routeId: route1.id } });
|
|
await prisma.routeStop.createMany({ data: [
|
|
{ routeId: route1.id, stationId: addis.id, sequence: 1, distanceKm: 0 },
|
|
{ routeId: route1.id, stationId: sebeta.id, sequence: 2, distanceKm: 23 },
|
|
{ routeId: route1.id, stationId: labu.id, sequence: 3, distanceKm: 45 },
|
|
{ routeId: route1.id, stationId: indode.id, sequence: 4, distanceKm: 62 },
|
|
{ routeId: route1.id, stationId: bishoftu.id, sequence: 5, distanceKm: 47 },
|
|
{ routeId: route1.id, stationId: mojo.id, sequence: 6, distanceKm: 73 },
|
|
{ routeId: route1.id, stationId: adama.id, sequence: 7, distanceKm: 99 },
|
|
{ routeId: route1.id, stationId: feto.id, sequence: 8, distanceKm: 145 },
|
|
{ routeId: route1.id, stationId: metahara.id, sequence: 9, distanceKm: 198 },
|
|
{ routeId: route1.id, stationId: awash.id, sequence: 10, distanceKm: 225 },
|
|
{ routeId: route1.id, stationId: mieso.id, sequence: 11, distanceKm: 305 },
|
|
{ routeId: route1.id, stationId: bike.id, sequence: 12, distanceKm: 375 },
|
|
{ routeId: route1.id, stationId: direDawa.id, sequence: 13, distanceKm: 453 },
|
|
{ routeId: route1.id, stationId: arawa.id, sequence: 14, distanceKm: 520 },
|
|
{ routeId: route1.id, stationId: adigala.id, sequence: 15, distanceKm: 580 },
|
|
{ routeId: route1.id, stationId: aysha.id, sequence: 16, distanceKm: 656 },
|
|
{ routeId: route1.id, stationId: dawanle.id, sequence: 17, distanceKm: 680 },
|
|
{ routeId: route1.id, stationId: alisabieh.id, sequence: 18, distanceKm: 700 },
|
|
{ routeId: route1.id, stationId: holhol.id, sequence: 19, distanceKm: 730 },
|
|
{ routeId: route1.id, stationId: nagad.id, sequence: 20, distanceKm: 750 },
|
|
{ routeId: route1.id, stationId: djibouti.id, sequence: 21, distanceKm: 756 },
|
|
]});
|
|
|
|
// Route Fare Rules (with passenger categories)
|
|
await prisma.routeFareRule.deleteMany({ where: { routeId: route1.id } });
|
|
await prisma.routeFareRule.createMany({ data: [
|
|
{ routeId: route1.id, serviceClass: 'ECONOMY_REGULAR', passengerCategory: 'ADULT', baseFareMinor: 45000, validFrom: new Date('2026-01-01') },
|
|
{ routeId: route1.id, serviceClass: 'ECONOMY_BED_LOWER', passengerCategory: 'ADULT', baseFareMinor: 65000, validFrom: new Date('2026-01-01') },
|
|
{ routeId: route1.id, serviceClass: 'VIP_BED_LOWER', passengerCategory: 'ADULT', baseFareMinor: 95000, validFrom: new Date('2026-01-01') },
|
|
{ routeId: route1.id, serviceClass: 'ECONOMY_REGULAR', passengerCategory: 'CHILD', baseFareMinor: 45000, validFrom: new Date('2026-01-01') },
|
|
{ routeId: route1.id, serviceClass: 'ECONOMY_BED_LOWER', passengerCategory: 'CHILD', baseFareMinor: 65000, validFrom: new Date('2026-01-01') },
|
|
{ routeId: route1.id, serviceClass: 'VIP_BED_LOWER', passengerCategory: 'CHILD', baseFareMinor: 95000, validFrom: new Date('2026-01-01') },
|
|
]});
|
|
|
|
// Train Services
|
|
const service301 = await prisma.trainService.upsert({ where: { number: '301' }, update: {}, create: { number: '301', name: 'Express 301' } });
|
|
const service302 = await prisma.trainService.upsert({ where: { number: '302' }, update: {}, create: { number: '302', name: 'Express 302' } });
|
|
|
|
// Trips (Multiple schedules) - Delete existing trips for clean seed
|
|
await prisma.trip.deleteMany({ where: { serviceId: { in: [service301.id, service302.id] } } });
|
|
const trip1 = await prisma.trip.create({
|
|
data: { serviceId: service301.id, originStationId: addis.id, destinationStationId: djibouti.id, departureAt: new Date('2026-06-15T08:00:00Z'), arrivalAt: new Date('2026-06-15T20:00:00Z'), durationMinutes: 720, stopsCount: 19 },
|
|
});
|
|
const trip2 = await prisma.trip.create({
|
|
data: { serviceId: service302.id, originStationId: djibouti.id, destinationStationId: addis.id, departureAt: new Date('2026-06-16T09:00:00Z'), arrivalAt: new Date('2026-06-16T21:30:00Z'), durationMinutes: 750, stopsCount: 19 },
|
|
});
|
|
const trip3 = await prisma.trip.create({
|
|
data: { serviceId: service301.id, originStationId: addis.id, destinationStationId: djibouti.id, departureAt: new Date('2026-06-17T07:30:00Z'), arrivalAt: new Date('2026-06-17T19:45:00Z'), durationMinutes: 735, stopsCount: 19 },
|
|
});
|
|
const trip4 = await prisma.trip.create({
|
|
data: { serviceId: service302.id, originStationId: djibouti.id, destinationStationId: addis.id, departureAt: new Date('2026-06-18T08:30:00Z'), arrivalAt: new Date('2026-06-18T21:00:00Z'), durationMinutes: 750, stopsCount: 19 },
|
|
});
|
|
|
|
// Trip Stop Times (Major stops only for brevity)
|
|
await prisma.tripStopTime.createMany({ data: [
|
|
{ tripId: trip1.id, stationId: addis.id, sequence: 1, plannedDepartureAt: new Date('2026-06-15T08:00:00Z'), status: 'UPCOMING' },
|
|
{ tripId: trip1.id, stationId: adama.id, sequence: 7, plannedArrivalAt: new Date('2026-06-15T09:30:00Z'), plannedDepartureAt: new Date('2026-06-15T09:45:00Z'), status: 'UPCOMING' },
|
|
{ tripId: trip1.id, stationId: awash.id, sequence: 10, plannedArrivalAt: new Date('2026-06-15T11:30:00Z'), plannedDepartureAt: new Date('2026-06-15T11:45:00Z'), status: 'UPCOMING' },
|
|
{ tripId: trip1.id, stationId: direDawa.id, sequence: 13, plannedArrivalAt: new Date('2026-06-15T15:00:00Z'), plannedDepartureAt: new Date('2026-06-15T15:20:00Z'), status: 'UPCOMING' },
|
|
{ tripId: trip1.id, stationId: aysha.id, sequence: 16, plannedArrivalAt: new Date('2026-06-15T18:00:00Z'), plannedDepartureAt: new Date('2026-06-15T18:10:00Z'), status: 'UPCOMING' },
|
|
{ tripId: trip1.id, stationId: djibouti.id, sequence: 21, plannedArrivalAt: new Date('2026-06-15T20:00:00Z'), status: 'UPCOMING' },
|
|
]});
|
|
|
|
// Coaches & Seats
|
|
for (const trip of [trip1, trip2, trip3, trip4]) {
|
|
const coaches = [
|
|
{ label: 'A', serviceClass: 'ECONOMY_REGULAR' as ServiceClass, seatCount: 60 },
|
|
{ label: 'B', serviceClass: 'ECONOMY_BED_LOWER' as ServiceClass, seatCount: 40 },
|
|
{ label: 'C', serviceClass: 'VIP_BED_LOWER' as ServiceClass, seatCount: 20 },
|
|
];
|
|
for (const { label, serviceClass, seatCount } of coaches) {
|
|
const coach = await prisma.coach.create({ data: { tripId: trip.id, label, serviceClass } });
|
|
const seats = [];
|
|
const rows = Math.ceil(seatCount / 4);
|
|
for (let row = 1; row <= rows; row++) {
|
|
for (const col of ['A', 'B', 'C', 'D']) {
|
|
if (seats.length >= seatCount) break;
|
|
seats.push({ coachId: coach.id, row, col, label: `${row}${col}`, kind: (row === 1 && col === 'A' ? 'ACCESSIBLE' : 'STANDARD') as SeatKind });
|
|
}
|
|
}
|
|
await prisma.seat.createMany({ data: seats });
|
|
}
|
|
}
|
|
|
|
// Fare Rules (All trips)
|
|
for (const trip of [trip1, trip2, trip3, trip4]) {
|
|
await prisma.fareRule.createMany({ data: [
|
|
{ tripId: trip.id, serviceClass: 'ECONOMY_REGULAR', baseFareMinor: 45000, validFrom: new Date('2026-01-01'), refundable: true },
|
|
{ tripId: trip.id, serviceClass: 'ECONOMY_BED_LOWER', baseFareMinor: 65000, validFrom: new Date('2026-01-01'), refundable: true },
|
|
{ tripId: trip.id, serviceClass: 'VIP_BED_LOWER', baseFareMinor: 95000, validFrom: new Date('2026-01-01'), refundable: true },
|
|
]});
|
|
}
|
|
|
|
// Users
|
|
const hash = await bcrypt.hash('password123', 10);
|
|
const adminHash = await bcrypt.hash('admin123', 10);
|
|
const agentHash = await bcrypt.hash('agent123', 10);
|
|
|
|
const adminUser = await prisma.user.upsert({ where: { email: 'admin@edr-platform.com' }, update: { passwordHash: adminHash, role: 'ADMIN' }, create: { fullName: 'EDR Admin', email: 'admin@edr-platform.com', phone: '+251900000000', passwordHash: adminHash, role: 'ADMIN' } });
|
|
|
|
const passengerUser = await prisma.user.upsert({ where: { email: 'kelemu@email.com' }, update: {}, create: { fullName: 'Kelemu Ketsela', email: 'kelemu@email.com', phone: '+251912345678', passwordHash: hash, nationality: 'Ethiopian', nationalId: 'ET123456789' } });
|
|
let passenger = await prisma.passenger.findUnique({ where: { userId: passengerUser.id } });
|
|
if (!passenger) {
|
|
passenger = await prisma.passenger.create({ data: { userId: passengerUser.id } });
|
|
await prisma.loyaltyAccount.create({ data: { passengerId: passenger.id, pointsBalance: 2450, tier: 'SILVER' } });
|
|
await prisma.walletAccount.create({ data: { passengerId: passenger.id, balanceMinor: 125000 } });
|
|
}
|
|
await prisma.userPreferences.upsert({ where: { userId: passengerUser.id }, update: {}, create: { userId: passengerUser.id, language: 'en' } });
|
|
|
|
const agentUser = await prisma.user.upsert({ where: { email: 'agent@edr-platform.com' }, update: { passwordHash: agentHash, role: 'AGENT' }, create: { fullName: 'Agent Abebe', email: 'agent@edr-platform.com', phone: '+251911111111', passwordHash: agentHash, role: 'AGENT' } });
|
|
await prisma.agent.upsert({ where: { userId: agentUser.id }, update: {}, create: { userId: agentUser.id, agentCode: 'AG001', stationId: addis.id, commissionRate: 5, active: true } });
|
|
|
|
// Baggage Allowance - Delete and recreate
|
|
await prisma.baggageAllowance.deleteMany({});
|
|
await prisma.baggageAllowance.createMany({ data: [
|
|
{ serviceClass: 'ECONOMY_REGULAR', maxWeightKg: 20, maxPiecesCount: 2, excessFeePerKg: 500 },
|
|
{ serviceClass: 'ECONOMY_BED_LOWER', maxWeightKg: 25, maxPiecesCount: 2, excessFeePerKg: 450 },
|
|
{ serviceClass: 'VIP_BED_LOWER', maxWeightKg: 30, maxPiecesCount: 3, excessFeePerKg: 400 },
|
|
]});
|
|
|
|
// Notification Templates
|
|
await prisma.notificationTemplate.upsert({ where: { code: 'BOOKING_CONFIRMED' }, update: {}, create: { code: 'BOOKING_CONFIRMED', channel: 'EMAIL', subject: 'Booking Confirmed', bodyTemplate: 'Your booking {{bookingRef}} is confirmed for {{tripDate}}.', active: true } });
|
|
await prisma.notificationTemplate.upsert({ where: { code: 'PAYMENT_SUCCESS' }, update: {}, create: { code: 'PAYMENT_SUCCESS', channel: 'SMS', bodyTemplate: 'Payment successful for {{bookingRef}}. Amount: {{amount}} ETB', active: true } });
|
|
await prisma.notificationTemplate.upsert({ where: { code: 'TRIP_REMINDER' }, update: {}, create: { code: 'TRIP_REMINDER', channel: 'PUSH', subject: 'Trip Reminder', bodyTemplate: 'Your trip departs in {{hours}} hours from {{station}}.', active: true } });
|
|
|
|
// Promotions
|
|
await prisma.promotion.upsert({ where: { code: 'WEEKEND15' }, update: {}, create: { title: 'Weekend Sale', subtitle: '15% off all trips', code: 'WEEKEND15', percentOff: 15, validUntil: new Date('2026-12-31'), ctaLabel: 'Book Now', active: true } });
|
|
await prisma.promotion.upsert({ where: { code: 'NEWUSER20' }, update: {}, create: { title: 'New User Bonus', code: 'NEWUSER20', percentOff: 20, validUntil: new Date('2026-12-31'), active: true } });
|
|
|
|
// FAQ - Delete and recreate for clean seed
|
|
await prisma.faqArticle.deleteMany({});
|
|
await prisma.faqCategory.deleteMany({});
|
|
const faqBooking = await prisma.faqCategory.create({ data: { title: 'Booking & Tickets', iconKey: 'confirmation_number' } });
|
|
const faqPayment = await prisma.faqCategory.create({ data: { title: 'Payment & Refunds', iconKey: 'payment' } });
|
|
|
|
await prisma.faqArticle.createMany({ data: [
|
|
{ categoryId: faqBooking.id, question: 'How do I book a train ticket?', answerMarkdown: 'Open Search, select origin and destination stations, choose date, select seats, and proceed to payment.', rank: 1 },
|
|
{ categoryId: faqBooking.id, question: 'Can I modify my booking?', answerMarkdown: 'Yes, you can modify your booking up to 24 hours before departure through the Bookings section.', rank: 2 },
|
|
{ categoryId: faqPayment.id, question: 'What payment methods are accepted?', answerMarkdown: 'We accept Telebirr, CBE Birr, eBirr, Card, and Wallet payments.', rank: 1 },
|
|
{ categoryId: faqPayment.id, question: 'How do refunds work?', answerMarkdown: 'Refunds are processed within 5-7 business days to your original payment method.', rank: 2 },
|
|
]});
|
|
|
|
// Menu Categories & Items - Delete and recreate
|
|
await prisma.menuItem.deleteMany({});
|
|
await prisma.menuCategory.deleteMany({});
|
|
const menuBeverages = await prisma.menuCategory.create({ data: { name: 'Beverages' } });
|
|
const menuSnacks = await prisma.menuCategory.create({ data: { name: 'Snacks' } });
|
|
|
|
await prisma.menuItem.createMany({ data: [
|
|
{ tripId: trip1.id, categoryId: menuBeverages.id, name: 'Coffee', priceMinor: 2500, available: true },
|
|
{ tripId: trip1.id, categoryId: menuBeverages.id, name: 'Tea', priceMinor: 2000, available: true },
|
|
{ tripId: trip1.id, categoryId: menuSnacks.id, name: 'Sandwich', priceMinor: 5000, available: true },
|
|
]});
|
|
|
|
// Station Crowd Signals - Delete and recreate
|
|
await prisma.stationCrowdSignal.deleteMany({});
|
|
await prisma.stationCrowdSignal.createMany({ data: [
|
|
{ stationId: addis.id, level: 'MODERATE', label: 'Moderate', statusLabel: 'Normal operations' },
|
|
{ stationId: adama.id, level: 'LOW', label: 'Low', statusLabel: 'Quiet' },
|
|
{ stationId: direDawa.id, level: 'LOW', label: 'Low', statusLabel: 'Quiet' },
|
|
{ stationId: djibouti.id, level: 'HIGH', label: 'High', statusLabel: 'Busy terminal' },
|
|
]});
|
|
|
|
// Fraud Detection Rules
|
|
await prisma.fraudRule.upsert({ where: { type: 'VELOCITY' }, update: {}, create: { type: 'VELOCITY', enabled: true, threshold: 3, config: { windowMinutes: 60, action: 'FLAG' } } });
|
|
await prisma.fraudRule.upsert({ where: { type: 'HIGH_VALUE' }, update: {}, create: { type: 'HIGH_VALUE', enabled: true, threshold: 500000, config: { action: 'REVIEW' } } });
|
|
await prisma.fraudRule.upsert({ where: { type: 'FAILED_PAYMENTS' }, update: {}, create: { type: 'FAILED_PAYMENTS', enabled: true, threshold: 5, config: { windowMinutes: 1440, action: 'BLOCK' } } });
|
|
|
|
// Loyalty Rewards (linked to loyalty account) - Delete and recreate
|
|
if (passenger) {
|
|
const loyaltyAccount = await prisma.loyaltyAccount.findUnique({ where: { passengerId: passenger.id } });
|
|
if (loyaltyAccount) {
|
|
await prisma.loyaltyReward.deleteMany({ where: { accountId: loyaltyAccount.id } });
|
|
await prisma.loyaltyReward.createMany({ data: [
|
|
{ accountId: loyaltyAccount.id, title: '10% Discount Voucher', costPoints: 1000, available: true, description: 'Get 10% off your next booking' },
|
|
{ accountId: loyaltyAccount.id, title: 'Free Upgrade to VIP', costPoints: 2500, available: true, description: 'Upgrade to VIP class on any trip' },
|
|
{ accountId: loyaltyAccount.id, title: '500 ETB Wallet Credit', costPoints: 5000, available: true, description: 'Add 500 ETB to your wallet' },
|
|
]});
|
|
}
|
|
}
|
|
|
|
// Currency Exchange Rates
|
|
await prisma.currencyExchangeRate.deleteMany({});
|
|
await prisma.currencyExchangeRate.createMany({ data: [
|
|
{ fromCurrency: 'ETB', toCurrency: 'ETB', rate: 1.0, effectiveDate: new Date() },
|
|
{ fromCurrency: 'ETB', toCurrency: 'DJF', rate: 3.25, effectiveDate: new Date() },
|
|
{ fromCurrency: 'ETB', toCurrency: 'USD', rate: 0.018, effectiveDate: new Date() },
|
|
{ fromCurrency: 'DJF', toCurrency: 'ETB', rate: 0.3077, effectiveDate: new Date() },
|
|
{ fromCurrency: 'USD', toCurrency: 'ETB', rate: 55.56, effectiveDate: new Date() },
|
|
]});
|
|
|
|
console.log('✅ Comprehensive seed complete');
|
|
console.log('\n📋 Seed Summary:');
|
|
console.log(' - 21 Stations (Complete Ethiopian-Djibouti Railway with country codes)');
|
|
console.log(' - 1 Route with 21 stops');
|
|
console.log(' - 2 Train services, 4 trips');
|
|
console.log(' - 3 Coaches per trip (Economy, Bed, VIP)');
|
|
console.log(' - Fare rules for ADULT and CHILD categories');
|
|
console.log(' - Currency exchange rates (ETB ↔ DJF, USD)');
|
|
console.log(' - 3 Users: Admin, Passenger (Silver tier + wallet), Agent');
|
|
console.log(' - 3 Fraud detection rules');
|
|
console.log(' - 3 Loyalty rewards');
|
|
console.log(' - Baggage rules, Notification templates, Promotions, FAQ');
|
|
console.log('\n🔑 Login Credentials:');
|
|
console.log(' Admin: admin@edr-platform.com / admin123');
|
|
console.log(' Passenger: kelemu@email.com / password123');
|
|
console.log(' Agent: agent@edr-platform.com / agent123');
|
|
console.log('\n🚉 Stations: Addis Ababa → Sebeta → Labu → Indode → Bishoftu → Mojo → Adama → Feto → Metahara → Awash → Mieso → Bike → Dire Dawa → Arawa → Adigala → Aysha → Dawanle → Alisabieh → Holhol → Nagad → Djibouti');
|
|
console.log('\n💰 Pricing: ADULT (≥5 years) = 100% fare | CHILD (<5 years) = First free, subsequent 100%');
|
|
console.log('\n💱 Currencies: ETB (transaction) | DJF, USD (display) | Rates: ETB→DJF=3.25, ETB→USD=0.018');
|
|
console.log('\n🔐 Verifayda: DISABLED (set VERIFAYDA_ENABLED=true in production)');
|
|
}
|
|
|
|
main().catch(console.error).finally(() => prisma.$disconnect());
|