diff --git a/src/order/dto/order.ts b/src/order/dto/order.ts index af397ec..1096e04 100644 --- a/src/order/dto/order.ts +++ b/src/order/dto/order.ts @@ -18,6 +18,7 @@ import { BaseDTO } from 'src/utils/dto/base.dto'; import { ResponseOrderProductPresentationDetailDTO } from 'src/products/dto/product-presentation.dto'; import { ResponseBranchDTO } from 'src/branch/dto/branch.dto'; import { OrderDeliveryEmployeeDTO } from './order-delivery.dto'; +import { PaymentMethod } from 'src/payments/entities/payment-information.entity'; export class CreateOrderDetailDTO { @ApiProperty({ description: 'ID of the product presentation' }) @@ -71,6 +72,14 @@ export class CreateOrderDTO { @Type(() => CreateOrderDetailDTO) products: CreateOrderDetailDTO[]; + @ApiProperty({ + description: 'Payment method (CASH, CARD, etc.)', + enum: PaymentMethod, + }) + @IsEnum(PaymentMethod) + @IsOptional() + paymentMethod: PaymentMethod; + constructor( type: OrderType, products: CreateOrderDetailDTO[], @@ -122,6 +131,15 @@ export class ResponseOrderDTO extends BaseDTO { @Expose() @ApiProperty({ description: 'Total price of the order' }) totalPrice: number; + + @Expose() + @ApiProperty({ + description: 'Payment method (CASH, CARD, etc.)', + enum: PaymentMethod, + }) + @IsEnum(PaymentMethod) + @IsOptional() + paymentMethod: PaymentMethod; } export class ResponseOrderDetailedDTO extends ResponseOrderDTO { diff --git a/src/order/entities/order.entity.ts b/src/order/entities/order.entity.ts index 9af5f7b..47a05b0 100644 --- a/src/order/entities/order.entity.ts +++ b/src/order/entities/order.entity.ts @@ -4,6 +4,8 @@ import { User } from 'src/user/entities/user.entity'; import { BaseModel, UUIDModel } from 'src/utils/entity'; import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; import { OrderDelivery, OrderDetailDelivery } from './order_delivery.entity'; +import { PaymentMethod } from 'src/payments/entities/payment-information.entity'; +import { PaymentConfirmation } from 'src/payments/entities/payment-confirmation.entity'; export enum OrderType { PICKUP = 'pickup', @@ -47,6 +49,20 @@ export class Order extends BaseModel { @OneToMany(() => OrderDelivery, (orderDelivery) => orderDelivery.order) orderDeliveries: OrderDelivery[]; + + @Column({ + type: 'enum', + enum: PaymentMethod, + name: 'payment_method', + default: PaymentMethod.CASH, + }) + paymentMethod: PaymentMethod; + + @OneToMany( + () => PaymentConfirmation, + (paymentConfirmation) => paymentConfirmation.order, + ) + paymentConfirmations: PaymentConfirmation[]; } @Entity() diff --git a/src/order/migrations/1746555455789-add-payment-method-order-migration.ts b/src/order/migrations/1746555455789-add-payment-method-order-migration.ts new file mode 100644 index 0000000..9269c6c --- /dev/null +++ b/src/order/migrations/1746555455789-add-payment-method-order-migration.ts @@ -0,0 +1,45 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddPaymentMethodOrderMigration1746555455789 + implements MigrationInterface +{ + name = 'AddPaymentMethodOrderMigration1746555455789'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "cart_item" DROP CONSTRAINT "FK_67a2e8406e01ffa24ff9026944e"`, + ); + await queryRunner.query( + `ALTER TABLE "payment_confirmation" ADD "order_id" uuid`, + ); + await queryRunner.query( + `CREATE TYPE "public"."order_payment_method_enum" AS ENUM('card', 'mobile_payment', 'bank_transfer', 'cash')`, + ); + await queryRunner.query( + `ALTER TABLE "order" ADD "payment_method" "public"."order_payment_method_enum" NOT NULL DEFAULT 'cash'`, + ); + await queryRunner.query( + `ALTER TABLE "payment_confirmation" ADD CONSTRAINT "FK_de3ea608b9f32c2184b551c554b" FOREIGN KEY ("order_id") REFERENCES "order"("id") ON DELETE RESTRICT ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "cart_item" ADD CONSTRAINT "FK_7a462d104c801f5e9bb63806c1e" FOREIGN KEY ("product_presentation_id") REFERENCES "product_presentation"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "cart_item" DROP CONSTRAINT "FK_7a462d104c801f5e9bb63806c1e"`, + ); + await queryRunner.query( + `ALTER TABLE "payment_confirmation" DROP CONSTRAINT "FK_de3ea608b9f32c2184b551c554b"`, + ); + await queryRunner.query(`ALTER TABLE "order" DROP COLUMN "payment_method"`); + await queryRunner.query(`DROP TYPE "public"."order_payment_method_enum"`); + await queryRunner.query( + `ALTER TABLE "payment_confirmation" DROP COLUMN "order_id"`, + ); + await queryRunner.query( + `ALTER TABLE "cart_item" ADD CONSTRAINT "FK_67a2e8406e01ffa24ff9026944e" FOREIGN KEY ("product_presentation_id") REFERENCES "product_presentation"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } +} diff --git a/src/order/order.service.ts b/src/order/order.service.ts index 21a0dcb..9bb6d65 100644 --- a/src/order/order.service.ts +++ b/src/order/order.service.ts @@ -118,6 +118,7 @@ export class OrderService { branch, type: createOrderDTO.type, totalPrice: totalPrice, + paymentMethod: createOrderDTO.paymentMethod, }); const order = await this.orderRepository.save(orderToCreate); const orderDetails = productsWithQuantity.map((product) => { diff --git a/src/payments/dto/payment-confirmation.dto.ts b/src/payments/dto/payment-confirmation.dto.ts index 81d4c8c..223b5c9 100644 --- a/src/payments/dto/payment-confirmation.dto.ts +++ b/src/payments/dto/payment-confirmation.dto.ts @@ -1,8 +1,8 @@ import { ApiProperty, IntersectionType } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; +import { IsString, IsUUID } from 'class-validator'; import { BaseDTO } from 'src/utils/dto/base.dto'; -export class CreatePaymentConfirmationDTO { +class PaymentConfirmationDTO { @ApiProperty() @IsString() bank: string; @@ -20,7 +20,14 @@ export class CreatePaymentConfirmationDTO { phoneNumber: string; } +export class CreatePaymentConfirmationDTO { + @ApiProperty() + @IsString() + @IsUUID() + orderId: string; +} + export class ResponsePaymentConfirmationDTO extends IntersectionType( - CreatePaymentConfirmationDTO, + PaymentConfirmationDTO, BaseDTO, ) {} diff --git a/src/payments/entities/payment-confirmation.entity.ts b/src/payments/entities/payment-confirmation.entity.ts index 01e11e1..0e98477 100644 --- a/src/payments/entities/payment-confirmation.entity.ts +++ b/src/payments/entities/payment-confirmation.entity.ts @@ -1,5 +1,6 @@ +import { Order } from 'src/order/entities/order.entity'; import { BaseModel } from 'src/utils/entity'; -import { Column, Entity } from 'typeorm'; +import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; @Entity('payment_confirmation') export class PaymentConfirmation extends BaseModel { @@ -15,5 +16,9 @@ export class PaymentConfirmation extends BaseModel { @Column({ type: 'character varying', name: 'phone_number' }) phoneNumber: string; - //order: Order; + @ManyToOne(() => Order, (order) => order.paymentConfirmations, { + onDelete: 'RESTRICT', + }) + @JoinColumn({ name: 'order_id' }) + order: Order; } diff --git a/src/payments/payment.module.ts b/src/payments/payment.module.ts index 3c9c2f1..cd2bfaf 100644 --- a/src/payments/payment.module.ts +++ b/src/payments/payment.module.ts @@ -7,11 +7,13 @@ import { PaymentConfirmationController } from './controllers/payment-confirmatio import { PaymentInformationService } from './services/payment-information.service'; import { PaymentConfirmationService } from './services/payment-confirmation.service'; import { AuthModule } from 'src/auth/auth.module'; +import { OrderModule } from 'src/order/order.module'; @Module({ imports: [ TypeOrmModule.forFeature([PaymentInformation, PaymentConfirmation]), AuthModule, + OrderModule, ], controllers: [PaymentInformationController, PaymentConfirmationController], providers: [PaymentInformationService, PaymentConfirmationService], diff --git a/src/payments/services/payment-confirmation.service.ts b/src/payments/services/payment-confirmation.service.ts index a36099a..ee12094 100644 --- a/src/payments/services/payment-confirmation.service.ts +++ b/src/payments/services/payment-confirmation.service.ts @@ -3,20 +3,26 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { PaymentConfirmation } from '../entities/payment-confirmation.entity'; import { CreatePaymentConfirmationDTO } from '../dto/payment-confirmation.dto'; +import { OrderService } from 'src/order/order.service'; @Injectable() export class PaymentConfirmationService { constructor( @InjectRepository(PaymentConfirmation) private paymentConfirmationRepository: Repository, + private orderService: OrderService, ) {} async create( createPaymentConfirmationDto: CreatePaymentConfirmationDTO, ): Promise { - const confirmation = this.paymentConfirmationRepository.create( - createPaymentConfirmationDto, + const order = await this.orderService.findOne( + createPaymentConfirmationDto.orderId, ); + const confirmation = this.paymentConfirmationRepository.create({ + ...createPaymentConfirmationDto, + order, + }); return this.paymentConfirmationRepository.save(confirmation); } } diff --git a/src/products/dto/product.dto.ts b/src/products/dto/product.dto.ts index 4b797dc..940bf17 100644 --- a/src/products/dto/product.dto.ts +++ b/src/products/dto/product.dto.ts @@ -89,6 +89,11 @@ export class ProductPresentationDTO extends BaseDTO { } export class ProductQueryDTO extends PaginationQueryDTO { + @IsOptional() + @Transform(({ value }: { value: string }) => (value ? value.split(',') : [])) + @IsUUID(undefined, { each: true }) + id: string[]; + @IsOptional() @Transform(({ value }: { value: string }) => (value ? value.split(',') : [])) @IsUUID(undefined, { each: true }) @@ -141,6 +146,7 @@ export class ProductQueryDTO extends PaginationQueryDTO { genericProductId?: string[], priceRange?: number[], isVisible?: boolean, + id?: string[], ) { super(page, limit); this.q = q ? q : ''; @@ -151,5 +157,6 @@ export class ProductQueryDTO extends PaginationQueryDTO { this.genericProductId = genericProductId ? genericProductId : []; this.priceRange = priceRange ? priceRange : []; this.isVisible = isVisible; + this.id = id ? id : []; } } diff --git a/src/products/products.controller.ts b/src/products/products.controller.ts index 6eda8a2..819a971 100644 --- a/src/products/products.controller.ts +++ b/src/products/products.controller.ts @@ -102,6 +102,12 @@ export class ProductsController { type: Boolean, example: true, }) + @ApiQuery({ + name: 'id', + required: false, + description: 'Filter by product presentation ID', + type: String, + }) @ApiOkResponse({ description: 'Products obtained correctly.', schema: { @@ -132,6 +138,7 @@ export class ProductsController { genericProductId, priceRange, isVisible, + id, } = pagination; const { products, total } = await this.productsServices.getProducts( page, @@ -144,6 +151,7 @@ export class ProductsController { genericProductId, priceRange, isVisible, + id, ); return { diff --git a/src/products/products.service.ts b/src/products/products.service.ts index 06e1acb..d01381e 100644 --- a/src/products/products.service.ts +++ b/src/products/products.service.ts @@ -21,6 +21,7 @@ export class ProductsService { genericProductIds?: string[], priceRange?: number[], isVisible?: boolean, + ids?: string[], ) { let where = {}; if (searchQuery) { @@ -32,6 +33,12 @@ export class ProductsService { ], }; } + if (ids && ids.length > 0) { + where = { + ...where, + id: In(ids), + }; + } if (categoryIds && categoryIds.length > 0) { where = { ...where,