import type { EntityManager } from '@mikro-orm/core'; import { Seeder } from '@mikro-orm/seeder'; import { Category } from '../modules/foods/entities/category.entity'; import { Food } from '../modules/foods/entities/food.entity'; import { Admin } from '../modules/admin/entities/admin.entity'; import { AdminRole } from '../modules/admin/entities/adminRole.entity'; import { Permission as PermissionEntity } from '../modules/roles/entities/permission.entity'; import { Role } from '../modules/roles/entities/role.entity'; import { Restaurant } from '../modules/restaurants/entities/restaurant.entity'; import { User } from '../modules/users/entities/user.entity'; import { Permission, PermissionTitles } from '../common/enums/permission.enum'; import { PaymentMethod } from '../modules/payments/entities/payment-method.entity'; import { RestaurantPaymentMethod } from '../modules/payments/entities/restaurant-payment-method.entity'; import { ShipmentMethod } from '../modules/shipments/entities/shipment-method.entity'; export class DatabaseSeeder extends Seeder { async run(em: EntityManager) { // 1. Create Permissions (English) const specialPermissions = [ Permission.CREATE_RESTAURANT, Permission.DELETE_RESTAURANT, Permission.UPDATE_RESTAURANT, ]; const permissions = Object.values(Permission).map(name => ({ name, title: PermissionTitles[name], group: specialPermissions.includes(name) ? 'special' : 'general', })); const createdPermissions: PermissionEntity[] = []; for (const permData of permissions) { let permission = await em.findOne(PermissionEntity, { name: permData.name }); if (!permission) { permission = em.create(PermissionEntity, permData); em.persist(permission); } else { // Update existing permission's group if it changed if (permission.group !== permData.group) { permission.group = permData.group; em.persist(permission); } } createdPermissions.push(permission); } // 2. Create Roles (English) let restaurantRole = await em.findOne(Role, { name: 'restaurant' }); if (!restaurantRole) { const role = em.create(Role, { name: 'restaurant', isSystem: false }); // Add all general permissions to restaurant role (excluding special permissions) const generalPermissions = createdPermissions.filter(p => p.group === 'general'); generalPermissions.forEach(p => role.permissions.add(p)); em.persist(role); restaurantRole = role; } const accountantRole = await em.findOne(Role, { name: 'accountant' }); if (!accountantRole) { const role = em.create(Role, { name: 'accountant', isSystem: false }); // Add financial permissions to accountant const financialPerms = createdPermissions.filter( p => (p.name as Permission) === Permission.VIEW_REPORTS || (p.name as Permission) === Permission.MANAGE_FINANCES || (p.name as Permission) === Permission.MANAGE_ORDERS, ); financialPerms.forEach(p => role.permissions.add(p)); em.persist(role); } const superAdminRole = await em.findOne(Role, { name: 'superAdmin' }); if (!superAdminRole) { const role = em.create(Role, { name: 'superAdmin', isSystem: true }); // Add restaurant management permissions to superAdmin const restaurantPerms = createdPermissions.filter( p => (p.name as Permission) === Permission.CREATE_RESTAURANT || (p.name as Permission) === Permission.DELETE_RESTAURANT || (p.name as Permission) === Permission.UPDATE_RESTAURANT, ); restaurantPerms.forEach(p => role.permissions.add(p)); em.persist(role); } await em.flush(); // Get roles after flush (refresh to ensure they're loaded) if (!restaurantRole) { restaurantRole = await em.findOne(Role, { name: 'restaurant' }); } await em.findOne(Role, { name: 'accountant' }); const superAdmin = await em.findOne(Role, { name: 'superAdmin' }); // 3. Create Payment Methods const zarinpalPaymentMethod = await em.findOne(PaymentMethod, { keyName: 'zarinpal' }); if (!zarinpalPaymentMethod) { const paymentMethod = em.create(PaymentMethod, { name: 'زرین‌پال', keyName: 'zarinpal', description: 'درگاه پرداخت زرین‌پال', isActive: true, order: 1, }); em.persist(paymentMethod); } await em.flush(); // 4. Create Shipment Methods (Farsi) const shipmentMethods = [ { name: 'dine-in', title: 'سرو در رستوران', description: 'سرو غذا در رستوران', isActive: true, }, { name: 'delivery-location', title: 'تحویل در محل', description: 'تحویل غذا در محل', isActive: true, }, { name: 'delivery-car', title: 'تحویل با ماشین', description: 'تحویل غذا با ماشین', isActive: true, }, { name: 'delivery-courier', title: 'تحویل با پیک', description: 'تحویل غذا با پیک', isActive: true, }, ]; const createdShipmentMethods: ShipmentMethod[] = []; for (const methodData of shipmentMethods) { let shipmentMethod = await em.findOne(ShipmentMethod, { name: methodData.name }); if (!shipmentMethod) { shipmentMethod = em.create(ShipmentMethod, methodData); em.persist(shipmentMethod); } else { // Update existing method if needed if (shipmentMethod.title !== methodData.title || shipmentMethod.description !== methodData.description) { shipmentMethod.title = methodData.title; shipmentMethod.description = methodData.description; em.persist(shipmentMethod); } } createdShipmentMethods.push(shipmentMethod); } await em.flush(); // 5. Create Restaurants (Farsi) const zhivanRestaurant = await em.findOne(Restaurant, { slug: 'zhivan' }); if (!zhivanRestaurant) { const restaurant = em.create(Restaurant, { name: 'ژیوان', slug: 'zhivan', isActive: true, phone: '09123456789', serviceArea: { type: 'Polygon', coordinates: [ [ [51.389, 35.6892], [51.39, 35.6892], [51.39, 35.6902], [51.389, 35.6902], [51.389, 35.6892], ], ], }, }); em.persist(restaurant); } const booteRestaurant = await em.findOne(Restaurant, { slug: 'boote' }); if (!booteRestaurant) { const restaurant = em.create(Restaurant, { name: 'بوته', slug: 'boote', isActive: true, phone: '09123456790', serviceArea: { type: 'Polygon', coordinates: [ [ [51.389, 35.6892], [51.39, 35.6892], [51.39, 35.6902], [51.389, 35.6902], [51.389, 35.6892], ], ], }, }); em.persist(restaurant); } await em.flush(); // Get restaurants after flush const zhivan = await em.findOne(Restaurant, { slug: 'zhivan' }); const boote = await em.findOne(Restaurant, { slug: 'boote' }); // 5.5. Create Restaurant Payment Methods const allRestaurants = [zhivan, boote].filter(Boolean) as Restaurant[]; const allPaymentMethods = await em.find(PaymentMethod, { isActive: true }); for (const restaurant of allRestaurants) { if (!restaurant) continue; for (const paymentMethod of allPaymentMethods) { const existing = await em.findOne(RestaurantPaymentMethod, { restaurant: { id: restaurant.id }, paymentMethod: { id: paymentMethod.id }, }); if (!existing) { const restaurantPaymentMethod = em.create(RestaurantPaymentMethod, { restaurant, paymentMethod, isActive: true, }); em.persist(restaurantPaymentMethod); } } } await em.flush(); // 6. Create Categories (Farsi) const categories = [ { title: 'غذای اصلی', restaurant: zhivan }, { title: 'پیش غذا', restaurant: zhivan }, { title: 'دسر', restaurant: zhivan }, { title: 'نوشیدنی', restaurant: zhivan }, { title: 'غذای اصلی', restaurant: boote }, { title: 'پیش غذا', restaurant: boote }, { title: 'دسر', restaurant: boote }, { title: 'نوشیدنی', restaurant: boote }, ]; const createdCategories: Category[] = []; for (const catData of categories) { if (!catData.restaurant) continue; const existing = await em.findOne(Category, { title: catData.title, restaurant: catData.restaurant, }); if (!existing) { const category = em.create(Category, { title: catData.title, restaurant: catData.restaurant, isActive: true, }); em.persist(category); createdCategories.push(category); } else { createdCategories.push(existing); } } await em.flush(); // 7. Create Foods (Farsi) const foods = [ // Zhivan Restaurant Foods { title: 'کباب کوبیده', desc: 'کباب کوبیده خوشمزه با برنج و کره', price: 150000, restaurant: zhivan, category: createdCategories[0], // غذای اصلی isActive: true, stock: 50, stockDefault: 50, }, { title: 'جوجه کباب', desc: 'جوجه کباب تازه با برنج', price: 120000, restaurant: zhivan, category: createdCategories[0], // غذای اصلی isActive: true, stock: 40, stockDefault: 40, }, { title: 'کباب بختیاری', desc: 'کباب بختیاری با برنج و کره', price: 180000, restaurant: zhivan, category: createdCategories[0], // غذای اصلی isActive: true, stock: 35, stockDefault: 35, }, { title: 'کباب برگ', desc: 'کباب برگ مرغوب با برنج', price: 160000, restaurant: zhivan, category: createdCategories[0], // غذای اصلی isActive: true, stock: 30, stockDefault: 30, }, { title: 'سالاد فصل', desc: 'سالاد تازه با سس مخصوص', price: 35000, restaurant: zhivan, category: createdCategories[1], // پیش غذا isActive: true, stock: 30, stockDefault: 30, }, { title: 'ماست و خیار', desc: 'ماست و خیار تازه', price: 25000, restaurant: zhivan, category: createdCategories[1], // پیش غذا isActive: true, stock: 25, stockDefault: 25, }, { title: 'ترشی', desc: 'ترشی مخلوط خانگی', price: 20000, restaurant: zhivan, category: createdCategories[1], // پیش غذا isActive: true, stock: 20, stockDefault: 20, }, { title: 'بستنی', desc: 'بستنی وانیلی خوشمزه', price: 25000, restaurant: zhivan, category: createdCategories[2], // دسر isActive: true, stock: 20, stockDefault: 20, }, { title: 'شیرینی', desc: 'شیرینی محلی', price: 30000, restaurant: zhivan, category: createdCategories[2], // دسر isActive: true, stock: 15, stockDefault: 15, }, { title: 'نوشابه', desc: 'نوشابه گازدار', price: 15000, restaurant: zhivan, category: createdCategories[3], // نوشیدنی isActive: true, stock: 100, stockDefault: 100, }, { title: 'دوغ', desc: 'دوغ محلی', price: 20000, restaurant: zhivan, category: createdCategories[3], // نوشیدنی isActive: true, stock: 50, stockDefault: 50, }, { title: 'آب معدنی', desc: 'آب معدنی', price: 10000, restaurant: zhivan, category: createdCategories[3], // نوشیدنی isActive: true, stock: 80, stockDefault: 80, }, // Boote Restaurant Foods { title: 'پیتزا مخصوص', desc: 'پیتزا با پنیر و قارچ', price: 180000, restaurant: boote, category: createdCategories[4], // غذای اصلی isActive: true, stock: 30, stockDefault: 30, }, { title: 'پیتزا پپرونی', desc: 'پیتزا پپرونی تند', price: 200000, restaurant: boote, category: createdCategories[4], // غذای اصلی isActive: true, stock: 25, stockDefault: 25, }, { title: 'برگر', desc: 'برگر با سیب زمینی سرخ کرده', price: 140000, restaurant: boote, category: createdCategories[4], // غذای اصلی isActive: true, stock: 25, stockDefault: 25, }, { title: 'برگر مخصوص', desc: 'برگر دوبل با پنیر', price: 180000, restaurant: boote, category: createdCategories[4], // غذای اصلی isActive: true, stock: 20, stockDefault: 20, }, { title: 'سوپ', desc: 'سوپ گرم و خوشمزه', price: 45000, restaurant: boote, category: createdCategories[5], // پیش غذا isActive: true, stock: 15, stockDefault: 15, }, { title: 'سالاد سزار', desc: 'سالاد سزار با سس مخصوص', price: 55000, restaurant: boote, category: createdCategories[5], // پیش غذا isActive: true, stock: 20, stockDefault: 20, }, { title: 'سیب زمینی سرخ کرده', desc: 'سیب زمینی سرخ کرده ترد', price: 40000, restaurant: boote, category: createdCategories[5], // پیش غذا isActive: true, stock: 30, stockDefault: 30, }, { title: 'کیک شکلاتی', desc: 'کیک شکلاتی خامه‌ای', price: 60000, restaurant: boote, category: createdCategories[6], // دسر isActive: true, stock: 10, stockDefault: 10, }, { title: 'چیزکیک', desc: 'چیزکیک وانیلی', price: 65000, restaurant: boote, category: createdCategories[6], // دسر isActive: true, stock: 12, stockDefault: 12, }, { title: 'نوشابه', desc: 'نوشابه گازدار', price: 15000, restaurant: boote, category: createdCategories[7], // نوشیدنی isActive: true, stock: 100, stockDefault: 100, }, { title: 'آبمیوه طبیعی', desc: 'آبمیوه تازه', price: 35000, restaurant: boote, category: createdCategories[7], // نوشیدنی isActive: true, stock: 40, stockDefault: 40, }, { title: 'قهوه', desc: 'قهوه اسپرسو', price: 45000, restaurant: boote, category: createdCategories[7], // نوشیدنی isActive: true, stock: 50, stockDefault: 50, }, ]; for (const foodData of foods) { if (foodData.restaurant && foodData.category) { const existing = await em.findOne(Food, { title: foodData.title, restaurant: foodData.restaurant, }); if (!existing) { const food = em.create(Food, { title: foodData.title, desc: foodData.desc, price: foodData.price, restaurant: foodData.restaurant, category: foodData.category, isActive: foodData.isActive, stock: foodData.stock, stockDefault: foodData.stockDefault, sat: false, sun: false, mon: false, breakfast: false, noon: false, dinner: false, discount: 0, rate: 0, inPlaceServe: false, pickupServe: false, }); em.persist(food); } } } await em.flush(); // 8. Create Admins (Farsi) const adminMorteza = await em.findOne(Admin, { phone: '09362532122' }); if (!adminMorteza && restaurantRole && zhivan) { const admin = em.create(Admin, { phone: '09362532122', firstName: 'مرتضی', lastName: 'مرتضایی', }); em.persist(admin); // Create AdminRole linking admin to role and restaurant const adminRole = em.create(AdminRole, { admin, role: restaurantRole, restaurant: zhivan, }); em.persist(adminRole); admin.roles.add(adminRole); } const adminHamid = await em.findOne(Admin, { phone: '09185290775' }); if (!adminHamid && restaurantRole && boote) { const admin = em.create(Admin, { phone: '09185290775', firstName: 'حمید', lastName: 'ضرقامی', }); em.persist(admin); // Create AdminRole linking admin to role and restaurant const adminRole = em.create(AdminRole, { admin, role: restaurantRole, restaurant: boote, }); em.persist(adminRole); admin.roles.add(adminRole); } const adminMehrdad = await em.findOne(Admin, { phone: '0912928395' }); if (!adminMehrdad && superAdmin) { const admin = em.create(Admin, { phone: '0912928395', firstName: 'مهرداد', lastName: 'مظفری', }); em.persist(admin); // Create AdminRole linking admin to role (no restaurant for superAdmin) const adminRole = em.create(AdminRole, { admin, role: superAdmin, restaurant: null, // SuperAdmin doesn't belong to a specific restaurant }); em.persist(adminRole); admin.roles.add(adminRole); } await em.flush(); // 9. Create Users (Farsi) const userMorteza = await em.findOne(User, { phone: '09362532122' }); if (!userMorteza && zhivan) { const user = em.create(User, { phone: '09362532122', firstName: 'مرتضی', lastName: 'مرتضایی', restaurant: zhivan, birthDate: new Date('1990-01-01'), marriageDate: new Date('2015-01-01'), isActive: true, gender: true, wallet: 0, points: 0, }); em.persist(user); } const userHamid = await em.findOne(User, { phone: '09185290775' }); if (!userHamid && boote) { const user = em.create(User, { phone: '09185290775', firstName: 'حمید', lastName: 'زرگامی', restaurant: boote, birthDate: new Date('1992-01-01'), marriageDate: new Date('2017-01-01'), isActive: true, gender: true, wallet: 0, points: 0, }); em.persist(user); } // Final flush await em.flush(); } }