This commit is contained in:
2026-01-14 12:00:13 +03:30
parent 4ab9de447a
commit 081f2cf4a7
26 changed files with 75 additions and 34927 deletions
+1 -19
View File
@@ -9,21 +9,12 @@ import { UploaderModule } from './modules/uploader/uploader.module';
import { AdminModule } from './modules/admin/admin.module';
import { ThrottlerModule } from '@nestjs/throttler';
import { ScheduleModule } from '@nestjs/schedule';
import { RestaurantsModule } from './modules/restaurants/restaurants.module';
import { productModule } from './modules/products/product.module';
import { CartModule } from './modules/cart/cart.module';
import { productModule } from './modules/product/product.module';
import { RolesModule } from './modules/roles/roles.module';
import { PaymentsModule } from './modules/payment/payments.module';
import { DeliveryModule } from './modules/delivery/delivery.module';
import { OrdersModule } from './modules/order/orders.module';
import { CouponModule } from './modules/coupons/coupon.module';
import { ReviewModule } from './modules/review/review.module';
import { NotificationsModule } from './modules/notification/notifications.module';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { PagerModule } from './modules/pager/pager.module';
import { ContactModule } from './modules/contact/contact.module';
import { InventoryModule } from './modules/inventory/inventory.module';
import { IconsModule } from './modules/icons/icons.module';
import { CacheModule } from '@nestjs/cache-manager';
import { cacheConfig } from './config/cache.config';
@@ -44,21 +35,12 @@ import { cacheConfig } from './config/cache.config';
},
]),
ScheduleModule.forRoot(),
RestaurantsModule,
productModule,
CartModule,
RolesModule,
PaymentsModule,
DeliveryModule,
OrdersModule,
CouponModule,
ReviewModule,
NotificationsModule,
EventEmitterModule.forRoot(),
PagerModule,
ContactModule,
InventoryModule,
IconsModule,
],
controllers: [],
providers: [],
+2 -2
View File
@@ -7,7 +7,7 @@ import { JwtModule } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { AdminModule } from '../admin/admin.module';
import { TokensService } from './services/tokens.service';
import { RestaurantsModule } from '../restaurants/restaurants.module';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { User } from '../user/entities/user.entity';
import { AdminAuthGuard } from './guards/adminAuth.guard';
@@ -33,7 +33,7 @@ import { NotificationsModule } from '../notification/notifications.module';
inject: [ConfigService],
}),
forwardRef(() => AdminModule),
forwardRef(() => RestaurantsModule),
MikroOrmModule.forFeature([User, RefreshToken]),
forwardRef(() => NotificationsModule),
@@ -2,11 +2,7 @@ import { IsString, IsNotEmpty, IsOptional, IsObject } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class SendNotificationDto {
@ApiProperty({ description: 'Restaurant ID (ULID)' })
@IsString()
@IsNotEmpty()
: string;
@ApiPropertyOptional({ description: 'User ID (ULID)' })
@IsString()
@IsOptional()
@@ -12,7 +12,6 @@ import { SmsProcessor } from './processors/sms.processor';
import { PushProcessor } from './processors/push.processor';
import { NotificationsController } from './controllers/notifications.controller';
import { User } from '../user/entities/user.entity';
import { Restaurant } from '../restaurants/entities/restaurant.entity';
import { AuthModule } from '../auth/auth.module';
import { ConfigService } from '@nestjs/config';
import { NotificationQueueNameEnum } from './constants/queue';
@@ -26,14 +25,13 @@ import { NotificationCrone } from './crone/notification.crone';
import { SmsService } from './services/sms.service';
import { SmsLog } from './entities/smsLogs.entity';
import { SmsLogRepository } from './repositories/sms-log.repository';
import { RestaurantsModule } from '../restaurants/restaurants.module';
import { SmsListeners } from './listeners/sms.listeners';
@Module({
imports: [
forwardRef(() => AuthModule),
JwtModule,
MikroOrmModule.forFeature([Notification, NotificationPreference, User, Restaurant, SmsLog]),
MikroOrmModule.forFeature([Notification, NotificationPreference, User, SmsLog]),
BullModule.registerQueue(
{ name: NotificationQueueNameEnum.SMS },
{ name: NotificationQueueNameEnum.PUSH },
@@ -52,7 +50,6 @@ import { SmsListeners } from './listeners/sms.listeners';
AdminModule,
forwardRef(() => UserModule),
UtilsModule,
forwardRef(() => RestaurantsModule),
],
controllers: [NotificationsController],
providers: [
+4 -10
View File
@@ -5,12 +5,8 @@ import { OrdersController } from './controllers/orders.controller';
import { Order } from './entities/order.entity';
import { OrderItem } from './entities/order-item.entity';
import { User } from '../user/entities/user.entity';
import { Restaurant } from '../restaurants/entities/restaurant.entity';
import { product } from '../products/entities/product.entity';
import { Product } from '../product/entities/product.entity';
import { UserAddress } from '../user/entities/user-address.entity';
import { PaymentMethod } from '../payment/entities/payment-method.entity';
import { CartModule } from '../cart/cart.module';
import { UtilsModule } from '../util/utils.module';
import { AuthModule } from '../auth/auth.module';
import { PaymentsModule } from '../payment/payments.module';
import { JwtModule } from '@nestjs/jwt';
@@ -19,20 +15,18 @@ import { OrderListeners } from './listeners/order.listeners';
import { OrdersCrone } from './crone/order.crone';
import { AdminModule } from '../admin/admin.module';
import { NotificationsModule } from '../notification/notifications.module';
import { InventoryModule } from '../inventory/inventory.module';
import { UserModule } from '../user/user.module';
import { UtilsModule } from '../util/utils.module';
@Module({
imports: [
MikroOrmModule.forFeature([Order, OrderItem, User, Restaurant, product, UserAddress, PaymentMethod]),
CartModule,
UtilsModule,
MikroOrmModule.forFeature([Order, OrderItem, User, Product, UserAddress]),
AuthModule,
forwardRef(() => PaymentsModule),
JwtModule,
AdminModule,
NotificationsModule,
InventoryModule,
UtilsModule,
forwardRef(() => UserModule)
],
controllers: [OrdersController],
+3 -10
View File
@@ -1,10 +1,6 @@
import { Module, forwardRef } from '@nestjs/common';
import { PaymentsService } from './services/payments.service';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { PaymentMethod } from './entities/payment-method.entity';
import { PaymentMethodRepository } from './repositories/payment-method.repository';
import { PaymentMethodService } from './services/payment-method.service';
import { Restaurant } from '../restaurants/entities/restaurant.entity';
import { PaymentsController } from './controllers/payments.controller';
import { AuthModule } from '../auth/auth.module';
import { JwtModule } from '@nestjs/jwt';
@@ -12,23 +8,20 @@ import { Payment } from './entities/payment.entity';
import { ZarinpalGateway } from './gateways/zarinpal.gateway';
import { GatewayManager } from './gateways/gateway.manager';
import { PaymentRepository } from './repositories/payment.repository';
import { InventoryModule } from '../inventory/inventory.module';
import { PaymentListeners } from './listeners/payment.listeners';
import { AdminModule } from '../admin/admin.module';
import { NotificationsModule } from '../notification/notifications.module';
import { OrdersModule } from '../order/orders.module';
@Module({
imports: [MikroOrmModule.forFeature([PaymentMethod, Payment, Restaurant]),
AuthModule, JwtModule, InventoryModule, AdminModule, NotificationsModule,
imports: [MikroOrmModule.forFeature([Payment]),
AuthModule, JwtModule, AdminModule, NotificationsModule,
forwardRef(() => OrdersModule)],
controllers: [PaymentsController],
providers: [PaymentsService, PaymentMethodService, PaymentMethodRepository,
providers: [PaymentsService,
PaymentRepository, ZarinpalGateway, GatewayManager, PaymentListeners],
exports: [
PaymentMethodRepository,
PaymentRepository,
PaymentMethodService,
PaymentsService,
// PaymentGatewayService,
],
+8 -41
View File
@@ -1,58 +1,25 @@
import { BullModule } from "@nestjs/bullmq";
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { TICKET } from "./constant";
import { TicketCategory } from "./entities/ticket-category.entity";
import { TicketMessage } from "./entities/ticket-message.entity";
import { Ticket } from "./entities/ticket.entity";
import { ReferralStrategyFactory } from "./providers/referral-strategy.factory";
import { TicketsService } from "./providers/tickets.service";
import { TicketCategoryRepository } from "./repositories/tickets-category.repository";
import { TicketMessagesRepository } from "./repositories/tickets-message.repository";
import { TicketsRepository } from "./repositories/tickets.repository";
import { TicketsController } from "./tickets.controller";
import { AccessLogsModule } from "../access-logs/access-logs.module";
import { UsersModule } from "../users/users.module";
import { TicketMessageAttachment } from "./entities/ticket-message-attachment";
import { TicketMessageAttachmentsRepository } from "./repositories/ticket-message-attachment.repository";
import { DanakServicesModule } from "../danak-services/danak-services.module";
import { NotificationModule } from "../notifications/notifications.module";
import { ReferralService } from "./providers/referral.service";
import { GroupStrategy } from "./strategies/group.strategy";
import { RoundRobinStrategy } from "./strategies/round-robin.strategy";
import { SupportPlansModule } from "../support-plans/support-plans.module";
import { TicketProcessor } from "./queue/ticket.processor";
import { LoggerModule } from "../logger/logger.module";
import { MikroOrmModule } from "@mikro-orm/nestjs";
@Module({
imports: [
TypeOrmModule.forFeature([Ticket, TicketCategory, TicketMessageAttachment, TicketMessage]),
AccessLogsModule,
MikroOrmModule.forFeature([Ticket]),
BullModule.registerQueue({ name: TICKET.QUEUE_NAME }),
UsersModule,
DanakServicesModule,
NotificationModule,
SupportPlansModule,
LoggerModule,
],
providers: [
GroupStrategy,
RoundRobinStrategy,
ReferralStrategyFactory,
{
provide: ReferralService,
useFactory: (strategyFactory: ReferralStrategyFactory) => new ReferralService(strategyFactory),
inject: [ReferralStrategyFactory],
},
TicketsService,
TicketsRepository,
TicketCategoryRepository,
TicketMessagesRepository,
TicketMessageAttachmentsRepository,
TicketProcessor,
],
providers: [TicketsService, TicketsRepository, TicketMessagesRepository],
controllers: [TicketsController],
exports: [TicketsService],
})
export class TicketsModule {}
export class TicketsModule { }
+11 -62
View File
@@ -1,20 +1,12 @@
import type { EntityManager } from '@mikro-orm/core';
import { Seeder } from '@mikro-orm/seeder';
import { Restaurant } from '../modules/restaurants/entities/restaurant.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 { productsSeeder } from './products.seeder';
import { ProductsSeeder } from './foods.seeder';
import { AdminsSeeder } from './admins.seeder';
import { UsersSeeder } from './users.seeder';
import { CouponsSeeder } from './coupons.seeder';
import { NotificationPreferencesSeeder } from './notification-preferences.seeder';
import { UserWalletsSeeder } from './user-wallets.seeder';
import { InventorySeeder } from './inventory.seeder';
// import { NotificationsSeeder } from './notifications.seeder';
export class DatabaseSeeder extends Seeder {
@@ -33,65 +25,27 @@ export class DatabaseSeeder extends Seeder {
const rolesMap = await rolesSeeder.run(em, permissions);
console.info(`[Seeder] Step 2/${TOTAL_STEPS}: Done`);
// 3. Create Restaurants
console.info(`[Seeder] Step 3/${TOTAL_STEPS}: Creating restaurants`);
const restaurantsSeeder = new RestaurantsSeeder();
const restaurantsMap = await restaurantsSeeder.run(em);
console.info(`[Seeder] Step 3/${TOTAL_STEPS}: Done`);
// 4. Create Payment Methods for all Restaurants
console.info(`[Seeder] Step 4/${TOTAL_STEPS}: Creating payment methods`);
const allRestaurants = await em.find(Restaurant, {});
const paymentMethodsSeeder = new PaymentMethodsSeeder();
await paymentMethodsSeeder.run(em, allRestaurants);
console.info(`[Seeder] Step 4/${TOTAL_STEPS}: Done`);
// 5. Create Delivery Methods for all Restaurants
console.info(`[Seeder] Step 5/${TOTAL_STEPS}: Creating delivery methods`);
const deliveryMethodsSeeder = new DeliveryMethodsSeeder();
await deliveryMethodsSeeder.run(em, allRestaurants);
console.info(`[Seeder] Step 5/${TOTAL_STEPS}: Done`);
// 6. Create Schedules for all Restaurants
console.info(`[Seeder] Step 6/${TOTAL_STEPS}: Creating schedules`);
const schedulesSeeder = new SchedulesSeeder();
await schedulesSeeder.run(em, allRestaurants);
console.info(`[Seeder] Step 6/${TOTAL_STEPS}: Done`);
// 7. Create Notification Preferences for all Restaurants
console.info(`[Seeder] Step 7/${TOTAL_STEPS}: Creating notification preferences`);
const notificationPreferencesSeeder = new NotificationPreferencesSeeder();
await notificationPreferencesSeeder.run(em, allRestaurants);
console.info(`[Seeder] Step 7/${TOTAL_STEPS}: Done`);
// 8. Create Categories
console.info(`[Seeder] Step 8/${TOTAL_STEPS}: Creating categories`);
const categoriesSeeder = new CategoriesSeeder();
const categoriesMap = await categoriesSeeder.run(em, restaurantsMap);
console.info(`[Seeder] Step 8/${TOTAL_STEPS}: Done`);
// console.info(`[Seeder] Step 8/${TOTAL_STEPS}: Creating categories`);
// const categoriesSeeder = new CategoriesSeeder();
// const categoriesMap = await categoriesSeeder.run(em, restaurantsMap);
// console.info(`[Seeder] Step 8/${TOTAL_STEPS}: Done`);
// 9. Create products
console.info(`[Seeder] Step 9/${TOTAL_STEPS}: Creating products`);
const productsSeeder = new productsSeeder();
await productsSeeder.run(em, restaurantsMap, categoriesMap);
const productsSeeder = new ProductsSeeder();
// await productsSeeder.run(em, categoriesMap);
console.info(`[Seeder] Step 9/${TOTAL_STEPS}: Done`);
// 10. Create Inventory for all products
console.info(`[Seeder] Step 10/${TOTAL_STEPS}: Creating inventory`);
const inventorySeeder = new InventorySeeder();
await inventorySeeder.run(em);
console.info(`[Seeder] Step 10/${TOTAL_STEPS}: Done`);
// 11. Create Coupons
console.info(`[Seeder] Step 11/${TOTAL_STEPS}: Creating coupons`);
const couponsSeeder = new CouponsSeeder();
await couponsSeeder.run(em, restaurantsMap);
console.info(`[Seeder] Step 11/${TOTAL_STEPS}: Done`);
// 12. Create Admins
console.info(`[Seeder] Step 12/${TOTAL_STEPS}: Creating admins`);
const adminsSeeder = new AdminsSeeder();
await adminsSeeder.run(em, rolesMap, restaurantsMap);
console.info(`[Seeder] Step 12/${TOTAL_STEPS}: Done`);
// 13. Create Users
@@ -100,11 +54,6 @@ export class DatabaseSeeder extends Seeder {
await usersSeeder.run(em);
console.info(`[Seeder] Step 13/${TOTAL_STEPS}: Done`);
// 14. Create User Wallets
console.info(`[Seeder] Step 14/${TOTAL_STEPS}: Creating user wallets`);
const userWalletsSeeder = new UserWalletsSeeder();
await userWalletsSeeder.run(em);
console.info(`[Seeder] Step 14/${TOTAL_STEPS}: Done`);
// 15. Create Notifications for admins and users for all restaurants
console.info(`[Seeder] Step 15/${TOTAL_STEPS}: (optional) creating notifications`);
+3 -15
View File
@@ -1,13 +1,11 @@
import type { EntityManager } from '@mikro-orm/core';
import { Admin } from '../modules/admin/entities/admin.entity';
import { AdminRole } from '../modules/admin/entities/adminRole.entity';
import type { Role } from '../modules/roles/entities/role.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { adminsData } from './data/admins.data';
import { normalizePhone } from '../modules/util/phone.util';
export class AdminsSeeder {
async run(em: EntityManager, rolesMap: Map<string, Role>, restaurantsMap: Map<string, Restaurant>): Promise<void> {
async run(em: EntityManager, rolesMap: Map<string, Role>): Promise<void> {
for (const adminData of adminsData) {
const existingAdmin = await em.findOne(Admin, { phone: adminData.phone });
if (existingAdmin) continue;
@@ -15,26 +13,16 @@ export class AdminsSeeder {
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 (!restaurant) continue;
const admin = em.create(Admin, {
phone: normalizePhone(adminData.phone),
firstName: adminData.firstName,
lastName: adminData.lastName,
role
});
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();
-42
View File
@@ -1,42 +0,0 @@
import type { EntityManager } from '@mikro-orm/core';
import { Coupon } from '../modules/coupons/entities/coupon.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { couponsData } from './data/coupons.data';
export class CouponsSeeder {
async run(em: EntityManager, restaurantsMap: Map<string, Restaurant>): Promise<void> {
for (const couponData of couponsData) {
const restaurant = restaurantsMap.get(couponData.restaurantSlug);
if (!restaurant) continue;
const existing = await em.findOne(Coupon, {
code: couponData.code,
restaurant: { id: restaurant.id },
});
if (!existing) {
const coupon = em.create(Coupon, {
restaurant,
code: couponData.code,
name: couponData.name,
description: couponData.description,
type: couponData.type,
value: couponData.value,
maxDiscount: couponData.maxDiscount,
minOrderAmount: couponData.minOrderAmount,
maxUses: couponData.maxUses,
maxUsesPerUser: couponData.maxUsesPerUser,
startDate: couponData.startDate ? new Date(couponData.startDate) : undefined,
endDate: couponData.endDate ? new Date(couponData.endDate) : undefined,
isActive: couponData.isActive,
usedCount: 0,
});
em.persist(coupon);
}
}
await em.flush();
}
}
-94
View File
@@ -1,94 +0,0 @@
import { CouponType } from '../../modules/coupons/interface/coupon';
export interface CouponData {
restaurantSlug: string;
code: string;
name: string;
description?: string;
type: CouponType;
value: number;
maxDiscount?: number;
minOrderAmount?: number;
maxUses?: number;
maxUsesPerUser?: number;
startDate?: string;
endDate?: string;
isActive: boolean;
}
export const couponsData: CouponData[] = [
{
restaurantSlug: 'zhivan',
code: 'WELCOME10',
name: 'خوش آمدگویی ۱۰٪',
description: 'تخفیف ۱۰ درصدی برای اولین خرید',
type: CouponType.PERCENTAGE,
value: 10,
maxDiscount: 50000,
minOrderAmount: 100000,
maxUses: 100,
maxUsesPerUser: 1,
isActive: true,
},
{
restaurantSlug: 'zhivan',
code: 'FIXED50K',
name: 'تخفیف ۵۰ هزار تومانی',
description: 'تخفیف ثابت ۵۰ هزار تومانی',
type: CouponType.FIXED,
value: 50000,
minOrderAmount: 200000,
maxUses: 50,
maxUsesPerUser: 2,
isActive: true,
},
{
restaurantSlug: 'zhivan',
code: 'WEEKEND20',
name: 'تعطیلات ۲۰٪',
description: 'تخفیف ۲۰ درصدی برای آخر هفته',
type: CouponType.PERCENTAGE,
value: 20,
maxDiscount: 100000,
minOrderAmount: 150000,
maxUses: 200,
isActive: true,
},
{
restaurantSlug: 'boote',
code: 'FIRST15',
name: 'اولین خرید ۱۵٪',
description: 'تخفیف ۱۵ درصدی برای اولین خرید',
type: CouponType.PERCENTAGE,
value: 15,
maxDiscount: 75000,
minOrderAmount: 100000,
maxUses: 100,
maxUsesPerUser: 1,
isActive: true,
},
{
restaurantSlug: 'boote',
code: 'SAVE30K',
name: 'صرفه جویی ۳۰ هزار',
description: 'تخفیف ثابت ۳۰ هزار تومانی',
type: CouponType.FIXED,
value: 30000,
minOrderAmount: 100000,
maxUses: 100,
isActive: true,
},
{
restaurantSlug: 'boote',
code: 'VIP25',
name: 'ویژه ۲۵٪',
description: 'تخفیف ویژه ۲۵ درصدی',
type: CouponType.PERCENTAGE,
value: 25,
maxDiscount: 150000,
minOrderAmount: 200000,
maxUses: 50,
maxUsesPerUser: 1,
isActive: true,
},
];
-45
View File
@@ -1,45 +0,0 @@
import { DeliveryMethodEnum } from '../../modules/delivery/interface/delivery';
export interface DeliveryMethodData {
method: DeliveryMethodEnum;
description: string;
deliveryFee: number;
minOrderPrice: number;
enabled: boolean;
order: number;
}
export const deliveryMethodsData: DeliveryMethodData[] = [
{
method: DeliveryMethodEnum.DineIn,
description: 'سرو غذا در رستوران',
deliveryFee: 0,
minOrderPrice: 0,
enabled: true,
order: 1,
},
{
method: DeliveryMethodEnum.CustomerPickup,
description: 'برداشت غذا از رستوران',
deliveryFee: 0,
minOrderPrice: 0,
enabled: true,
order: 2,
},
{
method: DeliveryMethodEnum.DeliveryCar,
description: 'تحویل غذا در خودرو',
deliveryFee: 5000,
minOrderPrice: 50000,
enabled: true,
order: 3,
},
{
method: DeliveryMethodEnum.DeliveryCourier,
description: 'تحویل غذا با پیک',
deliveryFee: 10000,
minOrderPrice: 100000,
enabled: true,
order: 4,
},
];
File diff suppressed because it is too large Load Diff
-35
View File
@@ -1,35 +0,0 @@
import { PaymentMethodEnum, PaymentGatewayEnum } from '../../modules/payment/interface/payment';
export interface PaymentMethodData {
method: PaymentMethodEnum;
description: string;
enabled: boolean;
order: number;
gateway: PaymentGatewayEnum | null;
merchantId?: string;
}
export const paymentMethodsData: PaymentMethodData[] = [
{
method: PaymentMethodEnum.Online,
description: 'درگاه پرداخت زرین‌پال',
enabled: true,
order: 1,
gateway: PaymentGatewayEnum.ZarinPal,
merchantId: 'b6f55bd0-6eae-4045-aeb8-07d084fa8f73',
},
{
method: PaymentMethodEnum.Cash,
description: 'پرداخت نقدی',
enabled: true,
order: 2,
gateway: null,
},
{
method: PaymentMethodEnum.Wallet,
description: 'پرداخت از کیف پول',
enabled: true,
order: 3,
gateway: null,
},
];
+18
View File
@@ -0,0 +1,18 @@
export interface productData {
title: string;
isActive: boolean;
categoryTitle: string,
images: string[];
desc: string[];
}
export const productsData: productData[] = [
{
title: 'قهوه ترک.. Turkish coffee',
desc: [''],
categoryTitle: 'قهوه ',
isActive: true,
images: ["https://dmenu.danakcorp.com/uploads/images/product/product_1715711043900.png"],
}
]
-10
View File
@@ -1,10 +0,0 @@
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
];
-52
View File
@@ -1,52 +0,0 @@
import type { EntityManager } from '@mikro-orm/core';
import { Delivery } from '../modules/delivery/entities/delivery.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { deliveryMethodsData } from './data/delivery-methods.data';
import { DeliveryFeeTypeEnum } from 'src/modules/delivery/interface/delivery';
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,
description: methodData.description,
deliveryFee: methodData.deliveryFee,
minOrderPrice: methodData.minOrderPrice,
enabled: methodData.enabled,
order: methodData.order,
deliveryFeeType: DeliveryFeeTypeEnum.FIXED,
perKilometerFee: null,
distanceBasedMinCost: null,
});
em.persist(delivery);
} else {
// Update existing delivery method if needed
if (
existing.description !== methodData.description ||
existing.deliveryFee !== methodData.deliveryFee ||
existing.minOrderPrice !== methodData.minOrderPrice ||
existing.order !== methodData.order
) {
existing.description = methodData.description;
existing.deliveryFee = methodData.deliveryFee;
existing.minOrderPrice = methodData.minOrderPrice;
existing.order = methodData.order;
em.persist(existing);
}
}
}
}
await em.flush();
}
}
+22 -48
View File
@@ -1,71 +1,45 @@
import type { EntityManager } from '@mikro-orm/core';
import { product } from '../modules/products/entities/product.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import type { Category } from '../modules/products/entities/category.entity';
import { MealType } from '../modules/products/interface/product.interface';
import { productsData } from './data/products.data';
import { Product } from '../modules/product/entities/product.entity';
import type { Category } from '../modules/product/entities/category.entity';
import { productsData } from './data/product.data';
export class productsSeeder {
export class ProductsSeeder {
async run(
em: EntityManager,
restaurantsMap: Map<string, Restaurant>,
categoriesMap: Map<string, Category>,
): Promise<void> {
for (const productData of productsData) {
const restaurant = restaurantsMap.get(productData.restaurantSlug);
const key = `${productData.restaurantSlug}-${productData.categoryTitle}`;
const key = `${productData.categoryTitle}`;
const normalize = (s: string) => (s || '').replace(/\s+/g, ' ').trim();
let category = categoriesMap.get(key);
// Fallback: try to find category by normalized title if exact key not found
if (!category) {
for (const [k, v] of categoriesMap.entries()) {
const parts = k.split('-');
const kSlug = parts[0];
const kTitle = parts.slice(1).join('-');
if (kSlug === productData.restaurantSlug && normalize(kTitle) === normalize(productData.categoryTitle)) {
category = v;
break;
}
}
return
}
if (!restaurant || !category) {
console.warn(
`Skipping product \"${productData.title}\" — missing ${!restaurant ? 'restaurant' : 'category'} for key ${key}`,
);
continue;
}
const existing = await em.findOne(product, {
const existing = await em.findOne(Product, {
title: productData.title,
restaurant,
});
if (!existing) {
const product = em.create(product, {
title: productData.title,
desc: productData.desc,
content: productData.content,
price: productData.price,
restaurant,
category,
isActive: productData.isActive,
// new fields on product entity
weekDays: productData.weekDays ?? [0, 1, 2, 3, 4, 5, 6],
mealTypes: productData.mealTypes ?? [MealType.BREAKFAST, MealType.LUNCH, MealType.DINNER, MealType.SNACK],
discount: productData.discount ?? 0,
score: productData.score ?? 0,
inPlaceServe: true,
pickupServe: true,
images: productData.images,
isSpecialOffer: productData.isSpecialOffer ?? false,
});
const product = em.create(Product, {
title: productData.title,
desc: productData.desc,
em.persist(product);
}
category,
isActive: productData.isActive,
prepareTime: 10,
images: productData.images,
});
em.persist(product);
}
await em.flush();
-29
View File
@@ -1,29 +0,0 @@
import type { EntityManager } from '@mikro-orm/core';
import { product } from '../modules/products/entities/product.entity';
import { Inventory } from '../modules/inventory/entities/inventory.entity';
export class InventorySeeder {
async run(em: EntityManager): Promise<void> {
const products = await em.find(product, {});
for (const product of products) {
// Check if inventory already exists for this product
const existingInventory = await em.findOne(Inventory, {
product: { id: product.id },
});
if (!existingInventory) {
const inventory = em.create(Inventory, {
product,
totalStock: 50,
availableStock: 50,
});
em.persist(inventory);
}
}
await em.flush();
}
}
@@ -1,36 +0,0 @@
import type { EntityManager } from '@mikro-orm/core';
import { NotificationPreference } from '../modules/notification/entities/notification-preference.entity';
import type { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { notificationPreferencesData } from './data/notification-preferences.data';
export class NotificationPreferencesSeeder {
async run(em: EntityManager, restaurants: Restaurant[]): Promise<void> {
for (const restaurant of restaurants) {
if (!restaurant) continue;
for (const preferenceData of notificationPreferencesData) {
const existing = await em.findOne(NotificationPreference, {
restaurant: { id: restaurant.id },
title: preferenceData.title,
});
if (!existing) {
const preference = em.create(NotificationPreference, {
restaurant,
title: preferenceData.title,
channels: preferenceData.channels,
});
em.persist(preference);
} else {
// Update existing preference if notification type changed
if (existing.channels.length !== preferenceData.channels.length) {
existing.channels = preferenceData.channels;
em.persist(existing);
}
}
}
}
await em.flush();
}
}
-68
View File
@@ -1,68 +0,0 @@
import type { EntityManager } from '@mikro-orm/core';
import { Notification } from '../modules/notification/entities/notification.entity';
import { Admin } from '../modules/admin/entities/admin.entity';
import { User } from '../modules/user/entities/user.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { NotifTitleEnum } from '../modules/notification/interfaces/notification.interface';
export class NotificationsSeeder {
/**
* Seeds notifications for all admins and users for every restaurant.
* Ensures up to 33 notifications exist per (restaurant, admin) and (restaurant, user).
*/
async run(em: EntityManager): Promise<void> {
const restaurants = await em.find(Restaurant, {});
const admins = await em.find(Admin, {});
const users = await em.find(User, {});
const titles = Object.values(NotifTitleEnum);
for (const restaurant of restaurants) {
if (!restaurant) continue;
// Admin notifications
for (const admin of admins) {
if (!admin) continue;
const existingCount = await em.count(Notification, {
restaurant: { id: restaurant.id },
admin: { id: admin.id },
});
const toCreate = Math.max(0, 33 - (existingCount || 0));
for (let i = 0; i < toCreate; i++) {
const notif = em.create(Notification, {
restaurant,
admin,
title: titles[i % titles.length] as NotifTitleEnum,
content: `Seeded notification ${i + 1} for admin ${admin.id} at ${restaurant.slug}`,
});
em.persist(notif);
}
}
// User notifications
for (const user of users) {
if (!user) continue;
const existingCount = await em.count(Notification, {
restaurant: { id: restaurant.id },
user: { id: user.id },
});
const toCreate = Math.max(0, 33 - (existingCount || 0));
for (let i = 0; i < toCreate; i++) {
const notif = em.create(Notification, {
restaurant,
user,
title: titles[i % titles.length] as NotifTitleEnum,
content: `Seeded notification ${i + 1} for user ${user.id} at ${restaurant.slug}`,
});
em.persist(notif);
}
}
}
await em.flush();
}
}
-52
View File
@@ -1,52 +0,0 @@
import type { EntityManager } from '@mikro-orm/core';
import { PaymentMethod } from '../modules/payment/entities/payment-method.entity';
import type { 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,
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.description !== methodData.description ||
existing.enabled !== methodData.enabled ||
existing.order !== methodData.order
) {
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();
}
}
-26
View File
@@ -1,26 +0,0 @@
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,
subscriptionStartDate: new Date('2026-01-01'),
subscriptionEndDate: new Date('2026-01-07'),
isActive: true,
});
em.persist(restaurant);
}
restaurantsMap.set(restaurantData.slug, restaurant);
}
await em.flush();
return restaurantsMap;
}
}
+1
View File
@@ -13,6 +13,7 @@ export class RolesSeeder {
if (!role) {
role = em.create(Role, {
name: roleConfig.name,
title:'',
isSystem: true,
});
-38
View File
@@ -1,38 +0,0 @@
import type { EntityManager } from '@mikro-orm/core';
import { Schedule } from '../modules/restaurants/entities/schedule.entity';
import type { 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, {
: restaurant.id,
weekDay,
openTime: timeSlot.openTime,
closeTime: timeSlot.closeTime,
});
if (!existing) {
const schedule = em.create(Schedule, {
: restaurant.id,
weekDay,
openTime: timeSlot.openTime,
closeTime: timeSlot.closeTime,
isActive: true,
});
em.persist(schedule);
}
}
}
}
await em.flush();
}
}
-36
View File
@@ -1,36 +0,0 @@
import type { EntityManager } from '@mikro-orm/core';
import { User } from '../modules/user/entities/user.entity';
import { Restaurant } from '../modules/restaurants/entities/restaurant.entity';
import { WalletTransaction } from '../modules/user/entities/wallet-transaction.entity';
import { WalletTransactionReason, WalletTransactionType } from 'src/modules/user/interface/wallet';
export class UserWalletsSeeder {
async run(em: EntityManager): Promise<void> {
const users = await em.find(User, {});
const restaurants = await em.find(Restaurant, {});
for (const user of users) {
for (const restaurant of restaurants) {
// Check if wallet already exists for this user-restaurant combination
const existingWallet = await em.findOne(WalletTransaction, {
user: { id: user.id },
restaurant: { id: restaurant.id },
});
if (!existingWallet) {
const wallet = em.create(WalletTransaction, {
user,
restaurant,
balance: 150000000,
amount: 150000000,
type: WalletTransactionType.CREDIT,
reason: WalletTransactionReason.DEPOSIT,
});
em.persist(wallet);
}
}
}
await em.flush();
}
}