Skip to content
This repository has been archived by the owner on Jul 8, 2024. It is now read-only.

Commit

Permalink
feat: Inclui campo "pago" no pedido (#91)
Browse files Browse the repository at this point in the history
* feat: Incluindo campo "pago" no pedido

* feat: Inclui campo "pago" no pedido

* refactor: Melhoria no teste da entidade pedido

* test: Adiciona teste para status de pagamento

* feature: Cria feature flag para o Mercado Pago

* refactor: Melhoria no método verificarPagamento

* Update pedido.use_case.spec.ts

* Update pedido.use_case.spec.ts

* Update pedido.mock.ts

* Update pedido.entity.spec.ts adicionando expect para o item pago

---------

Co-authored-by: Ana Paula Lopes <30847848+anaplopes@users.noreply.github.com>
  • Loading branch information
dannevesdantas and anaplopes authored Jan 23, 2024
1 parent ac27654 commit fae7938
Showing 16 changed files with 156 additions and 8 deletions.
1 change: 1 addition & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ DB_NAME=rms
DB_SSL=false

# Mercado Pago
ENABLE_MERCADOPAGO=false
ACCESS_TOKEN_MERCADOPAGO=
USER_ID_MERCADOPAGO=
EXTERNAL_POS_ID_MERCADOPAGO=
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ services:
DB_PASSWORD: ${DB_PASSWORD:-pgpwd}
DB_NAME: ${DB_NAME:-rms}
DB_SSL=: ${DB_SSL:-false}
ENABLE_MERCADOPAGO=: ${ENABLE_MERCADOPAGO:-false}
ACCESS_TOKEN_MERCADOPAGO: ${ACCESS_TOKEN_MERCADOPAGO:-}
USER_ID_MERCADOPAGO: ${USER_ID_MERCADOPAGO:-}
EXTERNAL_POS_ID_MERCADOPAGO: ${EXTERNAL_POS_ID_MERCADOPAGO:-}
1 change: 1 addition & 0 deletions k8s/development/bff/config.yaml
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ data:
DB_PASSWORD: "pgpwd"
DB_NAME: "rms"
DB_SSL: "false"
ENABLE_MERCADOPAGO: "false"
ACCESS_TOKEN_MERCADOPAGO: ""
USER_ID_MERCADOPAGO: ""
EXTERNAL_POS_ID_MERCADOPAGO: ""
1 change: 1 addition & 0 deletions k8s/production/bff/config.yaml
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ data:
DB_PASSWORD: ""
DB_NAME: ""
DB_SSL: ""
ENABLE_MERCADOPAGO: "true"
ACCESS_TOKEN_MERCADOPAGO: ""
USER_ID_MERCADOPAGO: ""
EXTERNAL_POS_ID_MERCADOPAGO: ""
3 changes: 3 additions & 0 deletions src/adapters/inbound/rest/v1/presenters/pedido.dto.ts
Original file line number Diff line number Diff line change
@@ -44,6 +44,9 @@ export class PedidoDTO {
@ApiProperty({ description: 'Status do pedido' })
statusPedido: string;

@ApiProperty({ description: 'Status do pagamento' })
pago: boolean;

@ApiProperty({ description: 'Cliente associado ao pedido' })
cliente: ClienteDTO;

3 changes: 3 additions & 0 deletions src/adapters/outbound/models/pedido.model.ts
Original file line number Diff line number Diff line change
@@ -28,6 +28,9 @@ export class PedidoModel {
@JoinColumn({ name: 'id_cliente' })
cliente: ClienteModel | null;

@Column({ name: 'pago', nullable: false })
pago: boolean;

@Column({ name: 'status_pedido', length: 20, nullable: false })
statusPedido: string;

Original file line number Diff line number Diff line change
@@ -83,6 +83,28 @@ describe('PedidoRepository', () => {
expect(result).toBe(pedidoModelMock);
});

it('deve alterar o status de pagamento do pedido', async () => {
const novoStatusPagamento = true;

pedidoTypeORMMock.findOne.mockResolvedValue(
Promise.resolve(pedidoModelMock),
);

const result = await pedidoRepository.editarStatusPagamento(
pedidoId,
novoStatusPagamento,
);

expect(pedidoTypeORMMock.update).toHaveBeenCalledWith(pedidoId, {
pago: novoStatusPagamento,
});
expect(pedidoTypeORMMock.findOne).toHaveBeenCalledWith({
where: { id: pedidoId },
relations: relations,
});
expect(result).toBe(pedidoModelMock);
});

it('deve editar o status de um pedido', async () => {
const novoStatusPedido = 'recebido';

14 changes: 14 additions & 0 deletions src/adapters/outbound/repositories/pedido/pedido.repository.ts
Original file line number Diff line number Diff line change
@@ -58,6 +58,20 @@ export class PedidoRepository implements IPedidoRepository {
});
}

async editarStatusPagamento(
pedidoId: string,
statusPagamento: boolean,
): Promise<PedidoModel> {
await this.pedidoRepository.update(pedidoId, {
pago: statusPagamento,
});

return await this.pedidoRepository.findOne({
where: { id: pedidoId },
relations: this.relations,
});
}

async buscarPedido(pedidoId: string): Promise<PedidoModel | null> {
return await this.pedidoRepository.findOne({
where: { id: pedidoId },
12 changes: 11 additions & 1 deletion src/domain/entities/pedido/pedido.entity.spec.ts
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ describe('PedidoEntity', () => {
let itensPedido: ItemPedidoEntity[];
let statusPedido: StatusPedido;
let numeroPedido: string;
let pago: boolean;
let cliente: ClienteEntity;
let id: string;

@@ -17,6 +18,7 @@ describe('PedidoEntity', () => {
itensPedido = [itemPedidoEntityMock];
statusPedido = StatusPedido.RECEBIDO;
numeroPedido = '05012024';
pago = true
cliente = clienteEntityMock;
id = '0a14aa4e-75e7-405f-8301-81f60646c93d';
});
@@ -26,23 +28,31 @@ describe('PedidoEntity', () => {
itensPedido,
statusPedido,
numeroPedido,
pago,
cliente,
id,
);

expect(pedido.itensPedido).toEqual(itensPedido);
expect(pedido.statusPedido).toEqual(statusPedido);
expect(pedido.numeroPedido).toEqual(numeroPedido);
expect(pedido.pago).toEqual(pago);
expect(pedido.cliente).toEqual(cliente);
expect(pedido.id).toEqual(id);
});

it('deve criar uma instância de PedidoEntity sem cliente e id', () => {
const pedido = new PedidoEntity(itensPedido, statusPedido, numeroPedido);
const pedido = new PedidoEntity(
itensPedido,
statusPedido,
numeroPedido,
pago,
);

expect(pedido.itensPedido).toEqual(itensPedido);
expect(pedido.statusPedido).toEqual(statusPedido);
expect(pedido.numeroPedido).toEqual(numeroPedido);
expect(pedido.pago).toEqual(pago);
expect(pedido.cliente).toBeUndefined();
expect(pedido.id).toBeUndefined();
});
11 changes: 11 additions & 0 deletions src/domain/entities/pedido/pedido.entity.ts
Original file line number Diff line number Diff line change
@@ -6,18 +6,21 @@ export class PedidoEntity {
private _itensPedido: ItemPedidoEntity[];
private _statusPedido: StatusPedido;
private _numeroPedido: string;
private _pago: boolean;
private _cliente?: ClienteEntity;
private _id?: string;

constructor(
itensPedido: ItemPedidoEntity[],
statusPedido: StatusPedido,
numeroPedido: string,
pago: boolean,
cliente?: ClienteEntity,
id?: string,
) {
this.id = id;
this.numeroPedido = numeroPedido;
this.pago = pago;
this.itensPedido = itensPedido;
this.cliente = cliente;
this.statusPedido = statusPedido;
@@ -47,6 +50,14 @@ export class PedidoEntity {
this._numeroPedido = numeroPedido;
}

get pago(): boolean {
return this._pago;
}

set pago(pago: boolean) {
this._pago = pago;
}

get cliente(): ClienteEntity | undefined {
return this._cliente;
}
2 changes: 2 additions & 0 deletions src/domain/factories/pedido/pedido.dto.factory.ts
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ export class PedidoDTOFactory implements IPedidoDTOFactory {
pedidoDTO.id = pedido.id;
pedidoDTO.numeroPedido = pedido.numeroPedido;
pedidoDTO.itensPedido = itensPedido;
pedidoDTO.pago = pedido.pago;
pedidoDTO.statusPedido = pedido.statusPedido;
pedidoDTO.cliente = cliente;
return pedidoDTO;
@@ -48,6 +49,7 @@ export class PedidoDTOFactory implements IPedidoDTOFactory {
pedidoDTO.id = pedido.id;
pedidoDTO.numeroPedido = pedido.numeroPedido;
pedidoDTO.itensPedido = itensPedido;
pedidoDTO.pago = pedido.pago;
pedidoDTO.statusPedido = pedido.statusPedido;
pedidoDTO.cliente = cliente;
return pedidoDTO;
1 change: 1 addition & 0 deletions src/domain/factories/pedido/pedido.factory.ts
Original file line number Diff line number Diff line change
@@ -88,6 +88,7 @@ export class PedidoFactory implements IPedidoFactory {
itensPedido,
StatusPedido.RECEBIDO,
numeroPedido,
false,
clienteEntity,
);
}
6 changes: 5 additions & 1 deletion src/domain/ports/pedido/pedido.repository.port.ts
Original file line number Diff line number Diff line change
@@ -3,11 +3,15 @@ import { PedidoModel } from 'src/adapters/outbound/models/pedido.model';

export interface IPedidoRepository {
criarPedido(pedido: PedidoEntity): Promise<PedidoModel>;
buscarPedido(pedidoId: string): Promise<PedidoModel | null>;
editarStatusPedido(
pedidoId: string,
statusPedido: string,
): Promise<PedidoModel | null>;
buscarPedido(pedidoId: string): Promise<PedidoModel | null>;
editarStatusPagamento(
pedidoId: string,
statusPagamento: boolean,
): Promise<PedidoModel | null>;
listarPedidos(): Promise<PedidoModel[] | []>;
listarPedidosRecebido(): Promise<PedidoModel[] | []>;
}
36 changes: 36 additions & 0 deletions src/domain/use_cases/pedido/pedido.use_case.spec.ts
Original file line number Diff line number Diff line change
@@ -13,9 +13,13 @@ import {
atualizaPedidoDTOMock,
pedidoDTOMock,
pedidoDTOFactoryMock,
configServiceMock,
mensagemGatewayPagamentoDTO,
pedidoGatewayPagamentoDTO,
} from 'src/mocks/pedido.mock';
import { IPedidoDTOFactory } from 'src/domain/ports/pedido/pedido.dto.factory.port';
import { PedidoNaoLocalizadoErro } from 'src/domain/exceptions/pedido.exception';
import { ConfigService } from '@nestjs/config';

describe('PedidoUseCase', () => {
let pedidoUseCase: PedidoUseCase;
@@ -41,6 +45,10 @@ describe('PedidoUseCase', () => {
provide: IPedidoDTOFactory,
useValue: pedidoDTOFactoryMock,
},
{
provide: ConfigService,
useValue: configServiceMock,
},
],
}).compile();

@@ -99,6 +107,34 @@ describe('PedidoUseCase', () => {
});
});

it('deve atualizar o status de pagamento do pedido com sucesso', async () => {
const idPedidoMercadoPago = '15171882961';
const topicMercadoPago = 'merchant_order';

pedidoRepositoryMock.buscarPedido.mockReturnValue(pedidoModelMock);
pedidoRepositoryMock.editarStatusPedido.mockReturnValue(pedidoModelMock);
pedidoDTOFactoryMock.criarPedidoDTO.mockReturnValue(pedidoDTOMock);
gatewayPagamentoServiceMock.consultarPedido.mockReturnValue(pedidoGatewayPagamentoDTO);

const result = await pedidoUseCase.webhookPagamento(
idPedidoMercadoPago,
topicMercadoPago,
mensagemGatewayPagamentoDTO,
);

expect(pedidoRepositoryMock.editarStatusPagamento).toHaveBeenCalledWith(
pedidoId,
true,
);
expect(pedidoRepositoryMock.editarStatusPedido).toHaveBeenCalledWith(
pedidoId,
'em preparacao',
);
expect(result).toStrictEqual({
mensagem: 'Mensagem consumida com sucesso'
});
});

it('deve retornar erro ao editar um pedido não existe', async () => {
pedidoRepositoryMock.buscarPedido.mockReturnValue(null);

24 changes: 18 additions & 6 deletions src/domain/use_cases/pedido/pedido.use_case.ts
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import { IPedidoUseCase } from 'src/domain/ports/pedido/pedido.use_case.port';
import { IGatewayPagamentoService } from 'src/domain/ports/pedido/gatewaypag.service.port';
import { HTTPResponse } from 'src/utils/HTTPResponse';
import { PedidoModel } from 'src/adapters/outbound/models/pedido.model';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class PedidoUseCase implements IPedidoUseCase {
@@ -26,6 +27,7 @@ export class PedidoUseCase implements IPedidoUseCase {
private readonly pedidoFactory: IPedidoFactory,
@Inject(IGatewayPagamentoService)
private readonly gatewayPagamentoService: IGatewayPagamentoService,
private configService: ConfigService,
@Inject(IPedidoDTOFactory)
private readonly pedidoDTOFactory: IPedidoDTOFactory,
) {}
@@ -46,10 +48,14 @@ export class PedidoUseCase implements IPedidoUseCase {
const result = await this.pedidoRepository.criarPedido(pedidoEntity);
pedidoEntity.id = result.id;

const qrData = await this.gatewayPagamentoService.criarPedido(pedidoEntity);

const pedidoDTO = this.pedidoDTOFactory.criarPedidoDTO(result);
pedidoDTO.qrCode = qrData;

const mercadoPagoIsEnabled = this.configService.get<string>('ENABLE_MERCADOPAGO').toLowerCase() === 'true';

if (mercadoPagoIsEnabled) {
const qrData = await this.gatewayPagamentoService.criarPedido(pedidoEntity);
pedidoDTO.qrCode = qrData;
}

return {
mensagem: 'Pedido criado com sucesso',
@@ -101,6 +107,7 @@ export class PedidoUseCase implements IPedidoUseCase {
mensagem: MensagemGatewayPagamentoDTO,
): Promise<any> {
if (id && topic === 'merchant_order') {
console.log(mensagem);
const pedidoGatewayPag =
await this.gatewayPagamentoService.consultarPedido(id);
const idInternoPedido = pedidoGatewayPag.external_reference;
@@ -110,13 +117,17 @@ export class PedidoUseCase implements IPedidoUseCase {
if (!buscaPedido) {
throw new PedidoNaoLocalizadoErro('Pedido não localizado');
}
await this.pedidoRepository.editarStatusPagamento(
idInternoPedido,
true,
);
await this.pedidoRepository.editarStatusPedido(
idInternoPedido,
'em preparacao',
);
}
return {
mensagem: `Mensagem ${mensagem} consumida com sucesso`,
mensagem: 'Mensagem consumida com sucesso',
};
}
}
@@ -125,9 +136,10 @@ export class PedidoUseCase implements IPedidoUseCase {
pedidoGatewayPag: PedidoGatewayPagamentoDTO,
): boolean {
if (
pedidoGatewayPag.order_status === 'paid' &&
pedidoGatewayPag.status === 'closed' && // closed: Order with payments covering total amount.
pedidoGatewayPag.order_status === 'paid' && // paid: Order with the sum of all payments "approved", "chargeback" or "in_mediation", covers the order total amount.
pedidoGatewayPag.payments.every((payment) => {
return payment.status === 'approved';
return payment.status === 'approved'; // approved: The payment has been approved and accredited.
})
) {
return true;
Loading

0 comments on commit fae7938

Please sign in to comment.