This commit is contained in:
2025-11-21 18:37:09 +03:30
parent e2a04e6a50
commit 2c0ab601f3
22 changed files with 669 additions and 2 deletions
+2
View File
@@ -20,6 +20,7 @@ import { CartModule } from './modules/cart/cart.module';
import { RolesModule } from './modules/roles/roles.module';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { PaymentsModule } from './modules/payments/payments.module';
@Module({
imports: [
@@ -58,6 +59,7 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
FoodModule,
CartModule,
RolesModule,
PaymentsModule,
],
controllers: [],
providers: [],
-1
View File
@@ -11,7 +11,6 @@ export class HttpExceptionFilter implements ExceptionFilter {
// In GraphQL context, host is actually an ExecutionContext
const gqlContext = GqlExecutionContext.create(host as ExecutionContext);
if (gqlContext && gqlContext.getInfo()) {
console.log('GraphQL context**');
// For GraphQL, let the exception propagate - GraphQL will handle it automatically
// We can't use HTTP response methods in GraphQL context
return;
@@ -0,0 +1,19 @@
import { InputType, Field, Int } from '@nestjs/graphql';
@InputType()
export class CreatePaymentMethodInput {
@Field()
name!: string;
@Field({ nullable: true })
description?: string;
@Field({ nullable: true })
icon?: string;
@Field(() => Boolean, { nullable: true, defaultValue: true })
isActive?: boolean;
@Field(() => Int, { nullable: true })
order?: number;
}
@@ -0,0 +1,7 @@
import { InputType, Int, Field } from '@nestjs/graphql';
@InputType()
export class CreatePaymentInput {
@Field(() => Int, { description: 'Example field (placeholder)' })
exampleField: number;
}
@@ -0,0 +1,16 @@
import { InputType, Field } from '@nestjs/graphql';
@InputType()
export class CreateRestaurantPaymentMethodInput {
@Field()
restaurantId!: string;
@Field()
paymentMethodId!: string;
@Field({ nullable: true })
merchantId?: string;
@Field({ nullable: true })
isActive?: boolean;
}
@@ -0,0 +1,8 @@
import { CreatePaymentMethodInput } from './create-payment-method.input';
import { InputType, Field, PartialType } from '@nestjs/graphql';
@InputType()
export class UpdatePaymentMethodInput extends PartialType(CreatePaymentMethodInput) {
@Field()
id!: string;
}
@@ -0,0 +1,8 @@
import { CreatePaymentInput } from './create-payment.input';
import { InputType, Field, Int, PartialType } from '@nestjs/graphql';
@InputType()
export class UpdatePaymentInput extends PartialType(CreatePaymentInput) {
@Field(() => Int)
id: number;
}
@@ -0,0 +1,8 @@
import { CreateRestaurantPaymentMethodInput } from './create-restaurant-payment-method.input';
import { InputType, Field, PartialType } from '@nestjs/graphql';
@InputType()
export class UpdateRestaurantPaymentMethodInput extends PartialType(CreateRestaurantPaymentMethodInput) {
@Field()
id!: string;
}
@@ -0,0 +1,31 @@
import { Entity, Property } from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity';
import { ObjectType, Field, Int } from '@nestjs/graphql';
@ObjectType()
@Entity({ tableName: 'payment_methods' })
export class PaymentMethod extends BaseEntity {
@Field()
@Property()
name!: string;
@Field()
@Property()
keyName!: string;
@Field({ nullable: true })
@Property({ nullable: true })
description?: string;
@Field({ nullable: true })
@Property({ nullable: true })
icon?: string;
@Field(() => Boolean)
@Property({ default: true })
isActive: boolean = true;
@Field(() => Int, { nullable: true })
@Property({ type: 'int', nullable: true })
order?: number;
}
@@ -0,0 +1,7 @@
import { ObjectType, Field, Int } from '@nestjs/graphql';
@ObjectType()
export class Payment {
@Field(() => Int, { description: 'Example field (placeholder)' })
exampleField: number;
}
@@ -0,0 +1,26 @@
import { Entity, ManyToOne, Unique, Property } from '@mikro-orm/core';
import { BaseEntity } from '../../../common/entities/base.entity';
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
import { PaymentMethod } from './payment-method.entity';
import { ObjectType, Field } from '@nestjs/graphql';
@ObjectType()
@Entity({ tableName: 'restaurant_payment_methods' })
@Unique({ properties: ['restaurant', 'paymentMethod'] })
export class RestaurantPaymentMethod extends BaseEntity {
@ManyToOne(() => Restaurant)
restaurant!: Restaurant;
@Field(() => PaymentMethod, { nullable: true })
@ManyToOne(() => PaymentMethod)
paymentMethod!: PaymentMethod;
// Add merchant ID for payment gateway providers (e.g., ZarinPal)
@Field({ nullable: true })
@Property({ nullable: true })
merchantId?: string;
@Field({ nullable: true })
@Property({ type: 'boolean', default: true })
isActive: boolean = true;
}
+34
View File
@@ -0,0 +1,34 @@
import { Module } from '@nestjs/common';
import { PaymentsService } from './services/payments.service';
import { PaymentsResolver } from './resolvers/payments.resolver';
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 { PaymentMethodResolver } from './resolvers/payment-method.resolver';
import { RestaurantPaymentMethod } from './entities/restaurant-payment-method.entity';
import { RestaurantPaymentMethodRepository } from './repositories/restaurant-payment-method.repository';
import { RestaurantPaymentMethodService } from './services/restaurant-payment-method.service';
import { RestaurantPaymentMethodResolver } from './resolvers/restaurant-payment-method.resolver';
import { Restaurant } from '../restaurants/entities/restaurant.entity';
@Module({
imports: [MikroOrmModule.forFeature([PaymentMethod, RestaurantPaymentMethod, Restaurant])],
providers: [
PaymentsResolver,
PaymentsService,
PaymentMethodResolver,
PaymentMethodService,
PaymentMethodRepository,
RestaurantPaymentMethodResolver,
RestaurantPaymentMethodService,
RestaurantPaymentMethodRepository,
],
exports: [
PaymentMethodRepository,
PaymentMethodService,
RestaurantPaymentMethodRepository,
RestaurantPaymentMethodService,
],
})
export class PaymentsModule {}
@@ -0,0 +1,10 @@
import { Injectable } from '@nestjs/common';
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
import { PaymentMethod } from '../entities/payment-method.entity';
@Injectable()
export class PaymentMethodRepository extends EntityRepository<PaymentMethod> {
constructor(readonly em: EntityManager) {
super(em, PaymentMethod);
}
}
@@ -0,0 +1,10 @@
import { Injectable } from '@nestjs/common';
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
import { RestaurantPaymentMethod } from '../entities/restaurant-payment-method.entity';
@Injectable()
export class RestaurantPaymentMethodRepository extends EntityRepository<RestaurantPaymentMethod> {
constructor(readonly em: EntityManager) {
super(em, RestaurantPaymentMethod);
}
}
@@ -0,0 +1,35 @@
import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql';
import { PaymentMethodService } from '../services/payment-method.service';
import { PaymentMethod } from '../entities/payment-method.entity';
import { CreatePaymentMethodInput } from '../dto/create-payment-method.input';
import { UpdatePaymentMethodInput } from '../dto/update-payment-method.input';
@Resolver(() => PaymentMethod)
export class PaymentMethodResolver {
constructor(private readonly paymentMethodService: PaymentMethodService) {}
@Mutation(() => PaymentMethod)
createPaymentMethod(@Args('createPaymentMethodInput') createPaymentMethodInput: CreatePaymentMethodInput) {
return this.paymentMethodService.create(createPaymentMethodInput);
}
@Query(() => [PaymentMethod], { name: 'paymentMethods' })
findAll() {
return this.paymentMethodService.findAll();
}
@Query(() => PaymentMethod, { name: 'paymentMethod' })
findOne(@Args('id', { type: () => ID }) id: string) {
return this.paymentMethodService.findOne(id);
}
@Mutation(() => PaymentMethod)
updatePaymentMethod(@Args('updatePaymentMethodInput') updatePaymentMethodInput: UpdatePaymentMethodInput) {
return this.paymentMethodService.update(updatePaymentMethodInput.id, updatePaymentMethodInput);
}
@Mutation(() => PaymentMethod)
removePaymentMethod(@Args('id', { type: () => ID }) id: string) {
return this.paymentMethodService.remove(id);
}
}
@@ -0,0 +1,35 @@
import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql';
import { PaymentsService } from '../services/payments.service';
import { Payment } from '../entities/payment.entity';
import { CreatePaymentInput } from '../dto/create-payment.input';
import { UpdatePaymentInput } from '../dto/update-payment.input';
@Resolver(() => Payment)
export class PaymentsResolver {
constructor(private readonly paymentsService: PaymentsService) {}
@Mutation(() => Payment)
createPayment(@Args('createPaymentInput') createPaymentInput: CreatePaymentInput) {
return this.paymentsService.create(createPaymentInput);
}
@Query(() => [Payment], { name: 'payments' })
findAll() {
return this.paymentsService.findAll();
}
@Query(() => Payment, { name: 'payment' })
findOne(@Args('id', { type: () => Int }) id: number) {
return this.paymentsService.findOne(id);
}
@Mutation(() => Payment)
updatePayment(@Args('updatePaymentInput') updatePaymentInput: UpdatePaymentInput) {
return this.paymentsService.update(updatePaymentInput.id, updatePaymentInput);
}
@Mutation(() => Payment)
removePayment(@Args('id', { type: () => Int }) id: number) {
return this.paymentsService.remove(id);
}
}
@@ -0,0 +1,52 @@
import { Resolver, Query, Mutation, Args, ID } from '@nestjs/graphql';
import { RestaurantPaymentMethodService } from '../services/restaurant-payment-method.service';
import { RestaurantPaymentMethod } from '../entities/restaurant-payment-method.entity';
import { CreateRestaurantPaymentMethodInput } from '../dto/create-restaurant-payment-method.input';
import { UpdateRestaurantPaymentMethodInput } from '../dto/update-restaurant-payment-method.input';
@Resolver(() => RestaurantPaymentMethod)
export class RestaurantPaymentMethodResolver {
constructor(private readonly restaurantPaymentMethodService: RestaurantPaymentMethodService) {}
@Mutation(() => RestaurantPaymentMethod)
createRestaurantPaymentMethod(
@Args('createRestaurantPaymentMethodInput') createRestaurantPaymentMethodInput: CreateRestaurantPaymentMethodInput,
) {
return this.restaurantPaymentMethodService.create(createRestaurantPaymentMethodInput);
}
@Query(() => [RestaurantPaymentMethod], { name: 'restaurantPaymentMethods' })
findAll() {
return this.restaurantPaymentMethodService.findAll();
}
@Query(() => [RestaurantPaymentMethod], { name: 'restaurantPaymentMethodsByRestaurant' })
findByRestaurant(@Args('restaurantId', { type: () => ID }) restaurantId: string) {
return this.restaurantPaymentMethodService.findByRestaurant(restaurantId);
}
@Query(() => [RestaurantPaymentMethod], { name: 'restaurantPaymentMethodsByPaymentMethod' })
findByPaymentMethod(@Args('paymentMethodId', { type: () => ID }) paymentMethodId: string) {
return this.restaurantPaymentMethodService.findByPaymentMethod(paymentMethodId);
}
@Query(() => RestaurantPaymentMethod, { name: 'restaurantPaymentMethod' })
findOne(@Args('id', { type: () => ID }) id: string) {
return this.restaurantPaymentMethodService.findOne(id);
}
@Mutation(() => RestaurantPaymentMethod)
updateRestaurantPaymentMethod(
@Args('updateRestaurantPaymentMethodInput') updateRestaurantPaymentMethodInput: UpdateRestaurantPaymentMethodInput,
) {
return this.restaurantPaymentMethodService.update(
updateRestaurantPaymentMethodInput.id,
updateRestaurantPaymentMethodInput,
);
}
@Mutation(() => RestaurantPaymentMethod)
removeRestaurantPaymentMethod(@Args('id', { type: () => ID }) id: string) {
return this.restaurantPaymentMethodService.remove(id);
}
}
@@ -0,0 +1,48 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { EntityManager } from '@mikro-orm/postgresql';
import { PaymentMethod } from '../entities/payment-method.entity';
import { PaymentMethodRepository } from '../repositories/payment-method.repository';
import { CreatePaymentMethodInput } from '../dto/create-payment-method.input';
import { UpdatePaymentMethodInput } from '../dto/update-payment-method.input';
import { RequiredEntityData } from '@mikro-orm/core';
@Injectable()
export class PaymentMethodService {
constructor(
private readonly paymentMethodRepository: PaymentMethodRepository,
private readonly em: EntityManager,
) {}
async create(createPaymentMethodInput: CreatePaymentMethodInput): Promise<PaymentMethod> {
const paymentMethod = this.paymentMethodRepository.create(
createPaymentMethodInput as unknown as RequiredEntityData<PaymentMethod>,
);
await this.em.persistAndFlush(paymentMethod);
return paymentMethod;
}
async findAll(): Promise<PaymentMethod[]> {
return this.paymentMethodRepository.findAll();
}
async findOne(id: string): Promise<PaymentMethod> {
const paymentMethod = await this.paymentMethodRepository.findOne({ id });
if (!paymentMethod) {
throw new NotFoundException(`PaymentMethod with ID ${id} not found`);
}
return paymentMethod;
}
async update(id: string, updatePaymentMethodInput: UpdatePaymentMethodInput): Promise<PaymentMethod> {
const paymentMethod = await this.findOne(id);
this.em.assign(paymentMethod, updatePaymentMethodInput);
await this.em.flush();
return paymentMethod;
}
async remove(id: string): Promise<PaymentMethod> {
const paymentMethod = await this.findOne(id);
await this.em.removeAndFlush(paymentMethod);
return paymentMethod;
}
}
@@ -0,0 +1,26 @@
import { Injectable } from '@nestjs/common';
import { CreatePaymentInput } from '../dto/create-payment.input';
import { UpdatePaymentInput } from '../dto/update-payment.input';
@Injectable()
export class PaymentsService {
create(createPaymentInput: CreatePaymentInput) {
return 'This action adds a new payment';
}
findAll() {
return `This action returns all payments`;
}
findOne(id: number) {
return `This action returns a #${id} payment`;
}
update(id: number, updatePaymentInput: UpdatePaymentInput) {
return `This action updates a #${id} payment`;
}
remove(id: number) {
return `This action removes a #${id} payment`;
}
}
@@ -0,0 +1,134 @@
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { EntityManager } from '@mikro-orm/postgresql';
import { RestaurantPaymentMethod } from '../entities/restaurant-payment-method.entity';
import { RestaurantPaymentMethodRepository } from '../repositories/restaurant-payment-method.repository';
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
import { PaymentMethod } from '../entities/payment-method.entity';
import { CreateRestaurantPaymentMethodInput } from '../dto/create-restaurant-payment-method.input';
import { UpdateRestaurantPaymentMethodInput } from '../dto/update-restaurant-payment-method.input';
import { RequiredEntityData } from '@mikro-orm/core';
@Injectable()
export class RestaurantPaymentMethodService {
constructor(
private readonly restaurantPaymentMethodRepository: RestaurantPaymentMethodRepository,
private readonly em: EntityManager,
) {}
async create(
createRestaurantPaymentMethodInput: CreateRestaurantPaymentMethodInput,
): Promise<RestaurantPaymentMethod> {
// Check if restaurant exists
const restaurant = await this.em.findOne(Restaurant, { id: createRestaurantPaymentMethodInput.restaurantId });
if (!restaurant) {
throw new NotFoundException(`Restaurant with ID ${createRestaurantPaymentMethodInput.restaurantId} not found`);
}
// Check if payment method exists
const paymentMethod = await this.em.findOne(PaymentMethod, { id: createRestaurantPaymentMethodInput.paymentMethodId });
if (!paymentMethod) {
throw new NotFoundException(`PaymentMethod with ID ${createRestaurantPaymentMethodInput.paymentMethodId} not found`);
}
// Check if the relationship already exists
const existing = await this.restaurantPaymentMethodRepository.findOne({
restaurant: { id: createRestaurantPaymentMethodInput.restaurantId },
paymentMethod: { id: createRestaurantPaymentMethodInput.paymentMethodId },
});
if (existing) {
throw new ConflictException('This payment method is already assigned to this restaurant');
}
const restaurantPaymentMethod = this.restaurantPaymentMethodRepository.create({
restaurant,
paymentMethod,
merchantId: createRestaurantPaymentMethodInput.merchantId,
isActive: createRestaurantPaymentMethodInput.isActive ?? true,
} as unknown as RequiredEntityData<RestaurantPaymentMethod>);
await this.em.persistAndFlush(restaurantPaymentMethod);
return restaurantPaymentMethod;
}
async findAll(): Promise<RestaurantPaymentMethod[]> {
return this.restaurantPaymentMethodRepository.findAll({ populate: ['restaurant', 'paymentMethod'] });
}
async findByRestaurant(restaurantId: string): Promise<RestaurantPaymentMethod[]> {
return this.restaurantPaymentMethodRepository.find(
{ restaurant: { id: restaurantId } },
{ populate: ['restaurant', 'paymentMethod'] },
);
}
async findByPaymentMethod(paymentMethodId: string): Promise<RestaurantPaymentMethod[]> {
return this.restaurantPaymentMethodRepository.find(
{ paymentMethod: { id: paymentMethodId } },
{ populate: ['restaurant', 'paymentMethod'] },
);
}
async findOne(id: string): Promise<RestaurantPaymentMethod> {
const restaurantPaymentMethod = await this.restaurantPaymentMethodRepository.findOne(
{ id },
{ populate: ['restaurant', 'paymentMethod'] },
);
if (!restaurantPaymentMethod) {
throw new NotFoundException(`RestaurantPaymentMethod with ID ${id} not found`);
}
return restaurantPaymentMethod;
}
async update(
id: string,
updateRestaurantPaymentMethodInput: UpdateRestaurantPaymentMethodInput,
): Promise<RestaurantPaymentMethod> {
const restaurantPaymentMethod = await this.findOne(id);
if (updateRestaurantPaymentMethodInput.restaurantId) {
const restaurant = await this.em.findOne(Restaurant, { id: updateRestaurantPaymentMethodInput.restaurantId });
if (!restaurant) {
throw new NotFoundException(`Restaurant with ID ${updateRestaurantPaymentMethodInput.restaurantId} not found`);
}
restaurantPaymentMethod.restaurant = restaurant;
}
if (updateRestaurantPaymentMethodInput.paymentMethodId) {
const paymentMethod = await this.em.findOne(PaymentMethod, { id: updateRestaurantPaymentMethodInput.paymentMethodId });
if (!paymentMethod) {
throw new NotFoundException(`PaymentMethod with ID ${updateRestaurantPaymentMethodInput.paymentMethodId} not found`);
}
// Check if the new relationship already exists
const existing = await this.restaurantPaymentMethodRepository.findOne({
restaurant: { id: restaurantPaymentMethod.restaurant.id },
paymentMethod: { id: updateRestaurantPaymentMethodInput.paymentMethodId },
});
if (existing && existing.id !== id) {
throw new ConflictException('This payment method is already assigned to this restaurant');
}
restaurantPaymentMethod.paymentMethod = paymentMethod;
}
// Update merchantId and isActive if provided
if (updateRestaurantPaymentMethodInput.merchantId !== undefined) {
restaurantPaymentMethod.merchantId = updateRestaurantPaymentMethodInput.merchantId;
}
if (updateRestaurantPaymentMethodInput.isActive !== undefined) {
restaurantPaymentMethod.isActive = updateRestaurantPaymentMethodInput.isActive;
}
await this.em.flush();
return restaurantPaymentMethod;
}
async remove(id: string): Promise<RestaurantPaymentMethod> {
const restaurantPaymentMethod = await this.findOne(id);
await this.em.removeAndFlush(restaurantPaymentMethod);
return restaurantPaymentMethod;
}
}