seeder
This commit is contained in:
+40
-755
@@ -1,773 +1,58 @@
|
||||
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 { PaymentMethodEnum, PaymentGatewayEnum } from '../modules/payments/interface/payment';
|
||||
import { Delivery } from '../modules/delivery/entities/delivery.entity';
|
||||
import { DeliveryMethodEnum } from '../modules/delivery/interface/delivery';
|
||||
import { Schedule } from '../modules/restaurants/entities/schedule.entity';
|
||||
import { PermissionsSeeder } from './permissions.seeder';
|
||||
import { RolesSeeder } from './roles.seeder';
|
||||
import { RestaurantsSeeder } from './restaurants.seeder';
|
||||
import { PaymentMethodsSeeder } from './payment-methods.seeder';
|
||||
import { DeliveryMethodsSeeder } from './delivery-methods.seeder';
|
||||
import { SchedulesSeeder } from './schedules.seeder';
|
||||
import { CategoriesSeeder } from './categories.seeder';
|
||||
import { FoodsSeeder } from './foods.seeder';
|
||||
import { AdminsSeeder } from './admins.seeder';
|
||||
import { UsersSeeder } from './users.seeder';
|
||||
|
||||
export class DatabaseSeeder extends Seeder {
|
||||
async run(em: EntityManager) {
|
||||
// 1. Create Permissions (English)
|
||||
const specialPermissions = [
|
||||
Permission.CREATE_RESTAURANT,
|
||||
Permission.DELETE_RESTAURANT,
|
||||
Permission.UPDATE_RESTAURANT,
|
||||
];
|
||||
// 1. Create Permissions
|
||||
const permissionsSeeder = new PermissionsSeeder();
|
||||
const permissions = await permissionsSeeder.run(em);
|
||||
|
||||
const permissions = Object.values(Permission).map(name => ({
|
||||
name,
|
||||
title: PermissionTitles[name],
|
||||
group: specialPermissions.includes(name) ? 'special' : 'general',
|
||||
}));
|
||||
// 2. Create Roles
|
||||
const rolesSeeder = new RolesSeeder();
|
||||
const rolesMap = await rolesSeeder.run(em, permissions);
|
||||
|
||||
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);
|
||||
}
|
||||
// 3. Create Restaurants
|
||||
const restaurantsSeeder = new RestaurantsSeeder();
|
||||
const restaurantsMap = await restaurantsSeeder.run(em);
|
||||
|
||||
// 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. Payment Methods will be created per restaurant (see section 5.5)
|
||||
|
||||
// 4. Delivery methods will be created per restaurant (see section 5.5.5)
|
||||
|
||||
// 5. Create Restaurants (Farsi)
|
||||
const zhivanRestaurant = await em.findOne(Restaurant, { slug: 'zhivan' });
|
||||
if (!zhivanRestaurant) {
|
||||
const restaurant = em.create(Restaurant, {
|
||||
name: 'ژیوان',
|
||||
slug: 'zhivan',
|
||||
domain: 'zhivan.example.com',
|
||||
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',
|
||||
domain: 'boote.example.com',
|
||||
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 Payment Methods for each Restaurant
|
||||
// 4. Create Payment Methods for all Restaurants
|
||||
const allRestaurants = await em.find(Restaurant, {});
|
||||
const paymentMethodsSeeder = new PaymentMethodsSeeder();
|
||||
await paymentMethodsSeeder.run(em, allRestaurants);
|
||||
|
||||
const paymentMethodsData = [
|
||||
{
|
||||
title: 'پرداخت در محل',
|
||||
method: PaymentMethodEnum.CardOnDelivery,
|
||||
description: 'پرداخت در محل تحویل',
|
||||
enabled: true,
|
||||
order: 1,
|
||||
gateway: null,
|
||||
},
|
||||
{
|
||||
title: 'نقدی',
|
||||
method: PaymentMethodEnum.Cash,
|
||||
description: 'پرداخت نقدی',
|
||||
enabled: true,
|
||||
order: 2,
|
||||
gateway: null,
|
||||
},
|
||||
{
|
||||
title: 'زرینپال',
|
||||
method: PaymentMethodEnum.Online,
|
||||
description: 'درگاه پرداخت زرینپال',
|
||||
enabled: true,
|
||||
order: 3,
|
||||
gateway: PaymentGatewayEnum.ZarinPal,
|
||||
merchantId: 'test-merchant-id',
|
||||
},
|
||||
];
|
||||
// 5. Create Delivery Methods for all Restaurants
|
||||
const deliveryMethodsSeeder = new DeliveryMethodsSeeder();
|
||||
await deliveryMethodsSeeder.run(em, allRestaurants);
|
||||
|
||||
for (const restaurant of allRestaurants) {
|
||||
if (!restaurant) continue;
|
||||
// 6. Create Schedules for all Restaurants
|
||||
const schedulesSeeder = new SchedulesSeeder();
|
||||
await schedulesSeeder.run(em, allRestaurants);
|
||||
|
||||
for (const methodData of paymentMethodsData) {
|
||||
const existing = await em.findOne(PaymentMethod, {
|
||||
restaurant: { id: restaurant.id },
|
||||
method: methodData.method,
|
||||
});
|
||||
// 7. Create Categories
|
||||
const categoriesSeeder = new CategoriesSeeder();
|
||||
const categoriesMap = await categoriesSeeder.run(em, restaurantsMap);
|
||||
|
||||
if (!existing) {
|
||||
const paymentMethod = em.create(PaymentMethod, {
|
||||
restaurant,
|
||||
title: methodData.title,
|
||||
method: methodData.method,
|
||||
description: methodData.description,
|
||||
enabled: methodData.enabled,
|
||||
order: methodData.order,
|
||||
gateway: methodData.gateway,
|
||||
merchantId: methodData.merchantId,
|
||||
});
|
||||
em.persist(paymentMethod);
|
||||
} else {
|
||||
// Update existing payment method if needed
|
||||
if (
|
||||
existing.title !== methodData.title ||
|
||||
existing.description !== methodData.description ||
|
||||
existing.enabled !== methodData.enabled ||
|
||||
existing.order !== methodData.order
|
||||
) {
|
||||
existing.title = methodData.title;
|
||||
existing.description = methodData.description;
|
||||
existing.enabled = methodData.enabled;
|
||||
existing.order = methodData.order;
|
||||
if (methodData.gateway !== undefined) {
|
||||
existing.gateway = methodData.gateway;
|
||||
}
|
||||
if (methodData.merchantId !== undefined) {
|
||||
existing.merchantId = methodData.merchantId;
|
||||
}
|
||||
em.persist(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 8. Create Foods
|
||||
const foodsSeeder = new FoodsSeeder();
|
||||
await foodsSeeder.run(em, restaurantsMap, categoriesMap);
|
||||
|
||||
await em.flush();
|
||||
// 9. Create Admins
|
||||
const adminsSeeder = new AdminsSeeder();
|
||||
await adminsSeeder.run(em, rolesMap, restaurantsMap);
|
||||
|
||||
// 5.5.5. Create Delivery Methods for Restaurants
|
||||
const allRestaurantsForDeliveries = await em.find(Restaurant, {});
|
||||
const deliveryMethodsData = [
|
||||
{
|
||||
method: DeliveryMethodEnum.DineIn,
|
||||
title: 'سرو در رستوران',
|
||||
description: 'سرو غذا در رستوران',
|
||||
deliveryFee: 0,
|
||||
minOrderPrice: 0,
|
||||
enabled: true,
|
||||
order: 1,
|
||||
},
|
||||
{
|
||||
method: DeliveryMethodEnum.Pickup,
|
||||
title: 'تحویل در رستوران',
|
||||
description: 'تحویل غذا در محل',
|
||||
deliveryFee: 0,
|
||||
minOrderPrice: 0,
|
||||
enabled: true,
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
method: DeliveryMethodEnum.DeliveryCar,
|
||||
title: 'تحویل با ماشین',
|
||||
description: 'تحویل غذا با ماشین',
|
||||
deliveryFee: 5000,
|
||||
minOrderPrice: 50000,
|
||||
enabled: true,
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
method: DeliveryMethodEnum.DeliveryCourier,
|
||||
title: 'تحویل با پیک',
|
||||
description: 'تحویل غذا با پیک',
|
||||
deliveryFee: 10000,
|
||||
minOrderPrice: 100000,
|
||||
enabled: true,
|
||||
order: 4,
|
||||
},
|
||||
];
|
||||
|
||||
for (const restaurant of allRestaurantsForDeliveries) {
|
||||
if (!restaurant) continue;
|
||||
|
||||
for (const methodData of deliveryMethodsData) {
|
||||
const existing = await em.findOne(Delivery, {
|
||||
restaurant: { id: restaurant.id },
|
||||
method: methodData.method,
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
const delivery = em.create(Delivery, {
|
||||
restaurant,
|
||||
method: methodData.method,
|
||||
title: methodData.title,
|
||||
description: methodData.description,
|
||||
deliveryFee: methodData.deliveryFee,
|
||||
minOrderPrice: methodData.minOrderPrice,
|
||||
enabled: methodData.enabled,
|
||||
order: methodData.order,
|
||||
});
|
||||
em.persist(delivery);
|
||||
} else {
|
||||
// Update existing delivery method if needed
|
||||
if (
|
||||
existing.title !== methodData.title ||
|
||||
existing.description !== methodData.description ||
|
||||
existing.deliveryFee !== methodData.deliveryFee ||
|
||||
existing.minOrderPrice !== methodData.minOrderPrice ||
|
||||
existing.order !== methodData.order
|
||||
) {
|
||||
existing.title = methodData.title;
|
||||
existing.description = methodData.description;
|
||||
existing.deliveryFee = methodData.deliveryFee;
|
||||
existing.minOrderPrice = methodData.minOrderPrice;
|
||||
existing.order = methodData.order;
|
||||
em.persist(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
|
||||
// 5.6. Create Schedules for all Restaurants
|
||||
// For each restaurant, create 3 schedules per day (breakfast, lunch, dinner) for all 7 days
|
||||
const allRestaurantsForSchedules = await em.find(Restaurant, {});
|
||||
|
||||
// Define three time slots per day
|
||||
const timeSlots = [
|
||||
{ openTime: '08:00', closeTime: '11:00' }, // Breakfast
|
||||
{ openTime: '12:00', closeTime: '15:00' }, // Lunch
|
||||
{ openTime: '18:00', closeTime: '23:00' }, // Dinner
|
||||
];
|
||||
|
||||
for (const restaurant of allRestaurantsForSchedules) {
|
||||
if (!restaurant) continue;
|
||||
|
||||
// Create schedules for each day (0 = Sunday, 6 = Saturday)
|
||||
for (let weekDay = 0; weekDay <= 6; weekDay++) {
|
||||
// Create 3 schedules per day
|
||||
for (const timeSlot of timeSlots) {
|
||||
const existing = await em.findOne(Schedule, {
|
||||
restId: restaurant.id,
|
||||
weekDay,
|
||||
openTime: timeSlot.openTime,
|
||||
closeTime: timeSlot.closeTime,
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
const schedule = em.create(Schedule, {
|
||||
restId: restaurant.id,
|
||||
weekDay,
|
||||
openTime: timeSlot.openTime,
|
||||
closeTime: timeSlot.closeTime,
|
||||
isActive: true,
|
||||
});
|
||||
em.persist(schedule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
// 10. Create Users
|
||||
const usersSeeder = new UsersSeeder();
|
||||
await usersSeeder.run(em, restaurantsMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { Admin } from '../modules/admin/entities/admin.entity';
|
||||
import { AdminRole } from '../modules/admin/entities/adminRole.entity';
|
||||
import { Role } from '../modules/roles/entities/role.entity';
|
||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||
import { adminsData } from './data/admins.data';
|
||||
|
||||
export class AdminsSeeder {
|
||||
async run(
|
||||
em: EntityManager,
|
||||
rolesMap: Map<string, Role>,
|
||||
restaurantsMap: Map<string, Restaurant>,
|
||||
): Promise<void> {
|
||||
for (const adminData of adminsData) {
|
||||
const existingAdmin = await em.findOne(Admin, { phone: adminData.phone });
|
||||
if (existingAdmin) continue;
|
||||
|
||||
const role = rolesMap.get(adminData.roleName);
|
||||
if (!role) continue;
|
||||
|
||||
const restaurant = adminData.restaurantSlug
|
||||
? restaurantsMap.get(adminData.restaurantSlug)
|
||||
: null;
|
||||
|
||||
// For restaurant role, restaurant is required
|
||||
if (adminData.roleName === 'restaurant' && !restaurant) continue;
|
||||
|
||||
const admin = em.create(Admin, {
|
||||
phone: adminData.phone,
|
||||
firstName: adminData.firstName,
|
||||
lastName: adminData.lastName,
|
||||
});
|
||||
em.persist(admin);
|
||||
|
||||
// Create AdminRole linking admin to role and restaurant
|
||||
const adminRole = em.create(AdminRole, {
|
||||
admin,
|
||||
role,
|
||||
restaurant: adminData.restaurantSlug ? restaurant : null,
|
||||
});
|
||||
em.persist(adminRole);
|
||||
admin.roles.add(adminRole);
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { Category } from '../modules/foods/entities/category.entity';
|
||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||
import { categoriesData } from './data/categories.data';
|
||||
|
||||
export class CategoriesSeeder {
|
||||
async run(
|
||||
em: EntityManager,
|
||||
restaurantsMap: Map<string, Restaurant>,
|
||||
): Promise<Map<string, Category>> {
|
||||
const categoriesMap = new Map<string, Category>();
|
||||
|
||||
for (const catData of categoriesData) {
|
||||
const restaurant = restaurantsMap.get(catData.restaurantSlug);
|
||||
if (!restaurant) continue;
|
||||
|
||||
const existing = await em.findOne(Category, {
|
||||
title: catData.title,
|
||||
restaurant,
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
const category = em.create(Category, {
|
||||
title: catData.title,
|
||||
restaurant,
|
||||
isActive: true,
|
||||
});
|
||||
em.persist(category);
|
||||
categoriesMap.set(`${catData.restaurantSlug}-${catData.title}`, category);
|
||||
} else {
|
||||
categoriesMap.set(`${catData.restaurantSlug}-${catData.title}`, existing);
|
||||
}
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
return categoriesMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
export interface AdminData {
|
||||
phone: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
roleName: string;
|
||||
restaurantSlug: string | null;
|
||||
}
|
||||
|
||||
export const adminsData: AdminData[] = [
|
||||
{
|
||||
phone: '09362532122',
|
||||
firstName: 'مرتضی',
|
||||
lastName: 'مرتضایی',
|
||||
roleName: 'restaurant',
|
||||
restaurantSlug: 'zhivan',
|
||||
},
|
||||
{
|
||||
phone: '09185290775',
|
||||
firstName: 'حمید',
|
||||
lastName: 'ضرقامی',
|
||||
roleName: 'restaurant',
|
||||
restaurantSlug: 'boote',
|
||||
},
|
||||
{
|
||||
phone: '0912928395',
|
||||
firstName: 'مهرداد',
|
||||
lastName: 'مظفری',
|
||||
roleName: 'superAdmin',
|
||||
restaurantSlug: null,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface CategoryData {
|
||||
title: string;
|
||||
restaurantSlug: string;
|
||||
}
|
||||
|
||||
export const categoriesData: CategoryData[] = [
|
||||
{ title: 'غذای اصلی', restaurantSlug: 'zhivan' },
|
||||
{ title: 'پیش غذا', restaurantSlug: 'zhivan' },
|
||||
{ title: 'دسر', restaurantSlug: 'zhivan' },
|
||||
{ title: 'نوشیدنی', restaurantSlug: 'zhivan' },
|
||||
{ title: 'غذای اصلی', restaurantSlug: 'boote' },
|
||||
{ title: 'پیش غذا', restaurantSlug: 'boote' },
|
||||
{ title: 'دسر', restaurantSlug: 'boote' },
|
||||
{ title: 'نوشیدنی', restaurantSlug: 'boote' },
|
||||
];
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { DeliveryMethodEnum } from '../../modules/delivery/interface/delivery';
|
||||
|
||||
export interface DeliveryMethodData {
|
||||
method: DeliveryMethodEnum;
|
||||
title: string;
|
||||
description: string;
|
||||
deliveryFee: number;
|
||||
minOrderPrice: number;
|
||||
enabled: boolean;
|
||||
order: number;
|
||||
}
|
||||
|
||||
export const deliveryMethodsData: DeliveryMethodData[] = [
|
||||
{
|
||||
method: DeliveryMethodEnum.DineIn,
|
||||
title: 'سرو در رستوران',
|
||||
description: 'سرو غذا در رستوران',
|
||||
deliveryFee: 0,
|
||||
minOrderPrice: 0,
|
||||
enabled: true,
|
||||
order: 1,
|
||||
},
|
||||
{
|
||||
method: DeliveryMethodEnum.Pickup,
|
||||
title: 'تحویل در رستوران',
|
||||
description: 'تحویل غذا در محل',
|
||||
deliveryFee: 0,
|
||||
minOrderPrice: 0,
|
||||
enabled: true,
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
method: DeliveryMethodEnum.DeliveryCar,
|
||||
title: 'تحویل با ماشین',
|
||||
description: 'تحویل غذا با ماشین',
|
||||
deliveryFee: 5000,
|
||||
minOrderPrice: 50000,
|
||||
enabled: true,
|
||||
order: 3,
|
||||
},
|
||||
{
|
||||
method: DeliveryMethodEnum.DeliveryCourier,
|
||||
title: 'تحویل با پیک',
|
||||
description: 'تحویل غذا با پیک',
|
||||
deliveryFee: 10000,
|
||||
minOrderPrice: 100000,
|
||||
enabled: true,
|
||||
order: 4,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
export interface FoodData {
|
||||
title: string;
|
||||
desc: string;
|
||||
price: number;
|
||||
restaurantSlug: string;
|
||||
categoryTitle: string;
|
||||
isActive: boolean;
|
||||
stock: number;
|
||||
stockDefault: number;
|
||||
}
|
||||
|
||||
export const foodsData: FoodData[] = [
|
||||
// Zhivan Restaurant Foods
|
||||
{
|
||||
title: 'کباب کوبیده',
|
||||
desc: 'کباب کوبیده خوشمزه با برنج و کره',
|
||||
price: 150000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'غذای اصلی',
|
||||
isActive: true,
|
||||
stock: 50,
|
||||
stockDefault: 50,
|
||||
},
|
||||
{
|
||||
title: 'جوجه کباب',
|
||||
desc: 'جوجه کباب تازه با برنج',
|
||||
price: 120000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'غذای اصلی',
|
||||
isActive: true,
|
||||
stock: 40,
|
||||
stockDefault: 40,
|
||||
},
|
||||
{
|
||||
title: 'کباب بختیاری',
|
||||
desc: 'کباب بختیاری با برنج و کره',
|
||||
price: 180000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'غذای اصلی',
|
||||
isActive: true,
|
||||
stock: 35,
|
||||
stockDefault: 35,
|
||||
},
|
||||
{
|
||||
title: 'کباب برگ',
|
||||
desc: 'کباب برگ مرغوب با برنج',
|
||||
price: 160000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'غذای اصلی',
|
||||
isActive: true,
|
||||
stock: 30,
|
||||
stockDefault: 30,
|
||||
},
|
||||
{
|
||||
title: 'سالاد فصل',
|
||||
desc: 'سالاد تازه با سس مخصوص',
|
||||
price: 35000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'پیش غذا',
|
||||
isActive: true,
|
||||
stock: 30,
|
||||
stockDefault: 30,
|
||||
},
|
||||
{
|
||||
title: 'ماست و خیار',
|
||||
desc: 'ماست و خیار تازه',
|
||||
price: 25000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'پیش غذا',
|
||||
isActive: true,
|
||||
stock: 25,
|
||||
stockDefault: 25,
|
||||
},
|
||||
{
|
||||
title: 'ترشی',
|
||||
desc: 'ترشی مخلوط خانگی',
|
||||
price: 20000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'پیش غذا',
|
||||
isActive: true,
|
||||
stock: 20,
|
||||
stockDefault: 20,
|
||||
},
|
||||
{
|
||||
title: 'بستنی',
|
||||
desc: 'بستنی وانیلی خوشمزه',
|
||||
price: 25000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'دسر',
|
||||
isActive: true,
|
||||
stock: 20,
|
||||
stockDefault: 20,
|
||||
},
|
||||
{
|
||||
title: 'شیرینی',
|
||||
desc: 'شیرینی محلی',
|
||||
price: 30000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'دسر',
|
||||
isActive: true,
|
||||
stock: 15,
|
||||
stockDefault: 15,
|
||||
},
|
||||
{
|
||||
title: 'نوشابه',
|
||||
desc: 'نوشابه گازدار',
|
||||
price: 15000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'نوشیدنی',
|
||||
isActive: true,
|
||||
stock: 100,
|
||||
stockDefault: 100,
|
||||
},
|
||||
{
|
||||
title: 'دوغ',
|
||||
desc: 'دوغ محلی',
|
||||
price: 20000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'نوشیدنی',
|
||||
isActive: true,
|
||||
stock: 50,
|
||||
stockDefault: 50,
|
||||
},
|
||||
{
|
||||
title: 'آب معدنی',
|
||||
desc: 'آب معدنی',
|
||||
price: 10000,
|
||||
restaurantSlug: 'zhivan',
|
||||
categoryTitle: 'نوشیدنی',
|
||||
isActive: true,
|
||||
stock: 80,
|
||||
stockDefault: 80,
|
||||
},
|
||||
// Boote Restaurant Foods
|
||||
{
|
||||
title: 'پیتزا مخصوص',
|
||||
desc: 'پیتزا با پنیر و قارچ',
|
||||
price: 180000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'غذای اصلی',
|
||||
isActive: true,
|
||||
stock: 30,
|
||||
stockDefault: 30,
|
||||
},
|
||||
{
|
||||
title: 'پیتزا پپرونی',
|
||||
desc: 'پیتزا پپرونی تند',
|
||||
price: 200000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'غذای اصلی',
|
||||
isActive: true,
|
||||
stock: 25,
|
||||
stockDefault: 25,
|
||||
},
|
||||
{
|
||||
title: 'برگر',
|
||||
desc: 'برگر با سیب زمینی سرخ کرده',
|
||||
price: 140000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'غذای اصلی',
|
||||
isActive: true,
|
||||
stock: 25,
|
||||
stockDefault: 25,
|
||||
},
|
||||
{
|
||||
title: 'برگر مخصوص',
|
||||
desc: 'برگر دوبل با پنیر',
|
||||
price: 180000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'غذای اصلی',
|
||||
isActive: true,
|
||||
stock: 20,
|
||||
stockDefault: 20,
|
||||
},
|
||||
{
|
||||
title: 'سوپ',
|
||||
desc: 'سوپ گرم و خوشمزه',
|
||||
price: 45000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'پیش غذا',
|
||||
isActive: true,
|
||||
stock: 15,
|
||||
stockDefault: 15,
|
||||
},
|
||||
{
|
||||
title: 'سالاد سزار',
|
||||
desc: 'سالاد سزار با سس مخصوص',
|
||||
price: 55000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'پیش غذا',
|
||||
isActive: true,
|
||||
stock: 20,
|
||||
stockDefault: 20,
|
||||
},
|
||||
{
|
||||
title: 'سیب زمینی سرخ کرده',
|
||||
desc: 'سیب زمینی سرخ کرده ترد',
|
||||
price: 40000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'پیش غذا',
|
||||
isActive: true,
|
||||
stock: 30,
|
||||
stockDefault: 30,
|
||||
},
|
||||
{
|
||||
title: 'کیک شکلاتی',
|
||||
desc: 'کیک شکلاتی خامهای',
|
||||
price: 60000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'دسر',
|
||||
isActive: true,
|
||||
stock: 10,
|
||||
stockDefault: 10,
|
||||
},
|
||||
{
|
||||
title: 'چیزکیک',
|
||||
desc: 'چیزکیک وانیلی',
|
||||
price: 65000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'دسر',
|
||||
isActive: true,
|
||||
stock: 12,
|
||||
stockDefault: 12,
|
||||
},
|
||||
{
|
||||
title: 'نوشابه',
|
||||
desc: 'نوشابه گازدار',
|
||||
price: 15000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'نوشیدنی',
|
||||
isActive: true,
|
||||
stock: 100,
|
||||
stockDefault: 100,
|
||||
},
|
||||
{
|
||||
title: 'آبمیوه طبیعی',
|
||||
desc: 'آبمیوه تازه',
|
||||
price: 35000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'نوشیدنی',
|
||||
isActive: true,
|
||||
stock: 40,
|
||||
stockDefault: 40,
|
||||
},
|
||||
{
|
||||
title: 'قهوه',
|
||||
desc: 'قهوه اسپرسو',
|
||||
price: 45000,
|
||||
restaurantSlug: 'boote',
|
||||
categoryTitle: 'نوشیدنی',
|
||||
isActive: true,
|
||||
stock: 50,
|
||||
stockDefault: 50,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { PaymentMethodEnum, PaymentGatewayEnum } from '../../modules/payments/interface/payment';
|
||||
|
||||
export interface PaymentMethodData {
|
||||
title: string;
|
||||
method: PaymentMethodEnum;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
order: number;
|
||||
gateway: PaymentGatewayEnum | null;
|
||||
merchantId?: string;
|
||||
}
|
||||
|
||||
export const paymentMethodsData: PaymentMethodData[] = [
|
||||
{
|
||||
title: 'پرداخت در محل',
|
||||
method: PaymentMethodEnum.CardOnDelivery,
|
||||
description: 'پرداخت در محل تحویل',
|
||||
enabled: true,
|
||||
order: 1,
|
||||
gateway: null,
|
||||
},
|
||||
{
|
||||
title: 'نقدی',
|
||||
method: PaymentMethodEnum.Cash,
|
||||
description: 'پرداخت نقدی',
|
||||
enabled: true,
|
||||
order: 2,
|
||||
gateway: null,
|
||||
},
|
||||
{
|
||||
title: 'زرینپال',
|
||||
method: PaymentMethodEnum.Online,
|
||||
description: 'درگاه پرداخت زرینپال',
|
||||
enabled: true,
|
||||
order: 3,
|
||||
gateway: PaymentGatewayEnum.ZarinPal,
|
||||
merchantId: 'test-merchant-id',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Permission, PermissionTitles } from '../../common/enums/permission.enum';
|
||||
|
||||
export const specialPermissions = [
|
||||
Permission.CREATE_RESTAURANT,
|
||||
Permission.DELETE_RESTAURANT,
|
||||
Permission.UPDATE_RESTAURANT,
|
||||
];
|
||||
|
||||
export const getPermissionsData = () => {
|
||||
return Object.values(Permission).map(name => ({
|
||||
name,
|
||||
title: PermissionTitles[name],
|
||||
group: specialPermissions.includes(name) ? 'special' : 'general',
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
export interface RestaurantData {
|
||||
name: string;
|
||||
slug: string;
|
||||
domain: string;
|
||||
isActive: boolean;
|
||||
phone: string;
|
||||
serviceArea: {
|
||||
type: 'Polygon';
|
||||
coordinates: number[][][];
|
||||
};
|
||||
}
|
||||
|
||||
export const restaurantsData: RestaurantData[] = [
|
||||
{
|
||||
name: 'ژیوان',
|
||||
slug: 'zhivan',
|
||||
domain: 'zhivan.example.com',
|
||||
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],
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'بوته',
|
||||
slug: 'boote',
|
||||
domain: 'boote.example.com',
|
||||
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],
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Permission } from '../../common/enums/permission.enum';
|
||||
|
||||
export interface RoleConfig {
|
||||
name: string;
|
||||
isSystem: boolean;
|
||||
permissionFilter: (permissionName: Permission) => boolean;
|
||||
}
|
||||
|
||||
export const rolesData: RoleConfig[] = [
|
||||
{
|
||||
name: 'restaurant',
|
||||
isSystem: false,
|
||||
permissionFilter: () => true, // Will be filtered by group in seeder
|
||||
},
|
||||
{
|
||||
name: 'accountant',
|
||||
isSystem: false,
|
||||
permissionFilter: (name: Permission) =>
|
||||
name === Permission.VIEW_REPORTS ||
|
||||
name === Permission.MANAGE_FINANCES ||
|
||||
name === Permission.MANAGE_ORDERS,
|
||||
},
|
||||
{
|
||||
name: 'superAdmin',
|
||||
isSystem: true,
|
||||
permissionFilter: (name: Permission) =>
|
||||
name === Permission.CREATE_RESTAURANT ||
|
||||
name === Permission.DELETE_RESTAURANT ||
|
||||
name === Permission.UPDATE_RESTAURANT,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface TimeSlot {
|
||||
openTime: string;
|
||||
closeTime: string;
|
||||
}
|
||||
|
||||
export const timeSlots: TimeSlot[] = [
|
||||
{ openTime: '08:00', closeTime: '11:00' }, // Breakfast
|
||||
{ openTime: '12:00', closeTime: '15:00' }, // Lunch
|
||||
{ openTime: '18:00', closeTime: '23:00' }, // Dinner
|
||||
];
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
export interface UserData {
|
||||
phone: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
restaurantSlug: string;
|
||||
birthDate: string;
|
||||
marriageDate: string;
|
||||
isActive: boolean;
|
||||
gender: boolean;
|
||||
wallet: number;
|
||||
points: number;
|
||||
}
|
||||
|
||||
export const usersData: UserData[] = [
|
||||
{
|
||||
phone: '09362532122',
|
||||
firstName: 'مرتضی',
|
||||
lastName: 'مرتضایی',
|
||||
restaurantSlug: 'zhivan',
|
||||
birthDate: '1990-01-01',
|
||||
marriageDate: '2015-01-01',
|
||||
isActive: true,
|
||||
gender: true,
|
||||
wallet: 0,
|
||||
points: 0,
|
||||
},
|
||||
{
|
||||
phone: '09185290775',
|
||||
firstName: 'حمید',
|
||||
lastName: 'زرگامی',
|
||||
restaurantSlug: 'boote',
|
||||
birthDate: '1992-01-01',
|
||||
marriageDate: '2017-01-01',
|
||||
isActive: true,
|
||||
gender: true,
|
||||
wallet: 0,
|
||||
points: 0,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { Delivery } from '../modules/delivery/entities/delivery.entity';
|
||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||
import { deliveryMethodsData } from './data/delivery-methods.data';
|
||||
|
||||
export class DeliveryMethodsSeeder {
|
||||
async run(em: EntityManager, restaurants: Restaurant[]): Promise<void> {
|
||||
for (const restaurant of restaurants) {
|
||||
if (!restaurant) continue;
|
||||
|
||||
for (const methodData of deliveryMethodsData) {
|
||||
const existing = await em.findOne(Delivery, {
|
||||
restaurant: { id: restaurant.id },
|
||||
method: methodData.method,
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
const delivery = em.create(Delivery, {
|
||||
restaurant,
|
||||
method: methodData.method,
|
||||
title: methodData.title,
|
||||
description: methodData.description,
|
||||
deliveryFee: methodData.deliveryFee,
|
||||
minOrderPrice: methodData.minOrderPrice,
|
||||
enabled: methodData.enabled,
|
||||
order: methodData.order,
|
||||
});
|
||||
em.persist(delivery);
|
||||
} else {
|
||||
// Update existing delivery method if needed
|
||||
if (
|
||||
existing.title !== methodData.title ||
|
||||
existing.description !== methodData.description ||
|
||||
existing.deliveryFee !== methodData.deliveryFee ||
|
||||
existing.minOrderPrice !== methodData.minOrderPrice ||
|
||||
existing.order !== methodData.order
|
||||
) {
|
||||
existing.title = methodData.title;
|
||||
existing.description = methodData.description;
|
||||
existing.deliveryFee = methodData.deliveryFee;
|
||||
existing.minOrderPrice = methodData.minOrderPrice;
|
||||
existing.order = methodData.order;
|
||||
em.persist(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { Food } from '../modules/foods/entities/food.entity';
|
||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||
import { Category } from '../modules/foods/entities/category.entity';
|
||||
import { foodsData } from './data/foods.data';
|
||||
|
||||
export class FoodsSeeder {
|
||||
async run(
|
||||
em: EntityManager,
|
||||
restaurantsMap: Map<string, Restaurant>,
|
||||
categoriesMap: Map<string, Category>,
|
||||
): Promise<void> {
|
||||
for (const foodData of foodsData) {
|
||||
const restaurant = restaurantsMap.get(foodData.restaurantSlug);
|
||||
const category = categoriesMap.get(`${foodData.restaurantSlug}-${foodData.categoryTitle}`);
|
||||
|
||||
if (!restaurant || !category) continue;
|
||||
|
||||
const existing = await em.findOne(Food, {
|
||||
title: foodData.title,
|
||||
restaurant,
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
const food = em.create(Food, {
|
||||
title: foodData.title,
|
||||
desc: foodData.desc,
|
||||
price: foodData.price,
|
||||
restaurant,
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { PaymentMethod } from '../modules/payments/entities/payment-method.entity';
|
||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||
import { paymentMethodsData } from './data/payment-methods.data';
|
||||
|
||||
export class PaymentMethodsSeeder {
|
||||
async run(em: EntityManager, restaurants: Restaurant[]): Promise<void> {
|
||||
for (const restaurant of restaurants) {
|
||||
if (!restaurant) continue;
|
||||
|
||||
for (const methodData of paymentMethodsData) {
|
||||
const existing = await em.findOne(PaymentMethod, {
|
||||
restaurant: { id: restaurant.id },
|
||||
method: methodData.method,
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
const paymentMethod = em.create(PaymentMethod, {
|
||||
restaurant,
|
||||
title: methodData.title,
|
||||
method: methodData.method,
|
||||
description: methodData.description,
|
||||
enabled: methodData.enabled,
|
||||
order: methodData.order,
|
||||
gateway: methodData.gateway,
|
||||
merchantId: methodData.merchantId,
|
||||
});
|
||||
em.persist(paymentMethod);
|
||||
} else {
|
||||
// Update existing payment method if needed
|
||||
if (
|
||||
existing.title !== methodData.title ||
|
||||
existing.description !== methodData.description ||
|
||||
existing.enabled !== methodData.enabled ||
|
||||
existing.order !== methodData.order
|
||||
) {
|
||||
existing.title = methodData.title;
|
||||
existing.description = methodData.description;
|
||||
existing.enabled = methodData.enabled;
|
||||
existing.order = methodData.order;
|
||||
if (methodData.gateway !== undefined) {
|
||||
existing.gateway = methodData.gateway;
|
||||
}
|
||||
if (methodData.merchantId !== undefined) {
|
||||
existing.merchantId = methodData.merchantId;
|
||||
}
|
||||
em.persist(existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { Permission as PermissionEntity } from '../modules/roles/entities/permission.entity';
|
||||
import { getPermissionsData } from './data/permissions.data';
|
||||
|
||||
export class PermissionsSeeder {
|
||||
async run(em: EntityManager): Promise<PermissionEntity[]> {
|
||||
const permissions = getPermissionsData();
|
||||
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);
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
return createdPermissions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||
import { restaurantsData } from './data/restaurants.data';
|
||||
|
||||
export class RestaurantsSeeder {
|
||||
async run(em: EntityManager): Promise<Map<string, Restaurant>> {
|
||||
const restaurantsMap = new Map<string, Restaurant>();
|
||||
|
||||
for (const restaurantData of restaurantsData) {
|
||||
let restaurant = await em.findOne(Restaurant, { slug: restaurantData.slug });
|
||||
if (!restaurant) {
|
||||
restaurant = em.create(Restaurant, restaurantData);
|
||||
em.persist(restaurant);
|
||||
}
|
||||
restaurantsMap.set(restaurantData.slug, restaurant);
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
return restaurantsMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { Role } from '../modules/roles/entities/role.entity';
|
||||
import { Permission } from '../common/enums/permission.enum';
|
||||
import { Permission as PermissionEntity } from '../modules/roles/entities/permission.entity';
|
||||
import { rolesData } from './data/roles.data';
|
||||
|
||||
export class RolesSeeder {
|
||||
async run(em: EntityManager, permissions: PermissionEntity[]): Promise<Map<string, Role>> {
|
||||
const rolesMap = new Map<string, Role>();
|
||||
|
||||
for (const roleConfig of rolesData) {
|
||||
let role = await em.findOne(Role, { name: roleConfig.name });
|
||||
if (!role) {
|
||||
role = em.create(Role, {
|
||||
name: roleConfig.name,
|
||||
isSystem: roleConfig.isSystem,
|
||||
});
|
||||
|
||||
// Add permissions based on role configuration
|
||||
// TypeScript knows role is not null here because we just created it
|
||||
const newRole = role;
|
||||
if (roleConfig.name === 'restaurant') {
|
||||
// Add all general permissions to restaurant role
|
||||
const generalPermissions = permissions.filter(p => p.group === 'general');
|
||||
generalPermissions.forEach(p => newRole.permissions.add(p));
|
||||
} else {
|
||||
// For other roles, use the permission filter
|
||||
const filteredPermissions = permissions.filter(p =>
|
||||
roleConfig.permissionFilter(p.name as Permission),
|
||||
);
|
||||
filteredPermissions.forEach(p => newRole.permissions.add(p));
|
||||
}
|
||||
|
||||
em.persist(newRole);
|
||||
}
|
||||
// Always add to map, whether existing or newly created
|
||||
rolesMap.set(roleConfig.name, role);
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
return rolesMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { Schedule } from '../modules/restaurants/entities/schedule.entity';
|
||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||
import { timeSlots } from './data/schedules.data';
|
||||
|
||||
export class SchedulesSeeder {
|
||||
async run(em: EntityManager, restaurants: Restaurant[]): Promise<void> {
|
||||
for (const restaurant of restaurants) {
|
||||
if (!restaurant) continue;
|
||||
|
||||
// Create schedules for each day (0 = Sunday, 6 = Saturday)
|
||||
for (let weekDay = 0; weekDay <= 6; weekDay++) {
|
||||
// Create 3 schedules per day
|
||||
for (const timeSlot of timeSlots) {
|
||||
const existing = await em.findOne(Schedule, {
|
||||
restId: restaurant.id,
|
||||
weekDay,
|
||||
openTime: timeSlot.openTime,
|
||||
closeTime: timeSlot.closeTime,
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
const schedule = em.create(Schedule, {
|
||||
restId: restaurant.id,
|
||||
weekDay,
|
||||
openTime: timeSlot.openTime,
|
||||
closeTime: timeSlot.closeTime,
|
||||
isActive: true,
|
||||
});
|
||||
em.persist(schedule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import { User } from '../modules/users/entities/user.entity';
|
||||
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
|
||||
import { usersData } from './data/users.data';
|
||||
|
||||
export class UsersSeeder {
|
||||
async run(em: EntityManager, restaurantsMap: Map<string, Restaurant>): Promise<void> {
|
||||
for (const userData of usersData) {
|
||||
const existingUser = await em.findOne(User, { phone: userData.phone });
|
||||
if (existingUser) continue;
|
||||
|
||||
const restaurant = restaurantsMap.get(userData.restaurantSlug);
|
||||
if (!restaurant) continue;
|
||||
|
||||
const user = em.create(User, {
|
||||
phone: userData.phone,
|
||||
firstName: userData.firstName,
|
||||
lastName: userData.lastName,
|
||||
restaurant,
|
||||
birthDate: new Date(userData.birthDate),
|
||||
marriageDate: new Date(userData.marriageDate),
|
||||
isActive: userData.isActive,
|
||||
gender: userData.gender,
|
||||
wallet: userData.wallet,
|
||||
points: userData.points,
|
||||
});
|
||||
em.persist(user);
|
||||
}
|
||||
|
||||
await em.flush();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user