From 92b627c4b464dfdaa7084c306fb62d86116c5ba3 Mon Sep 17 00:00:00 2001 From: Alexandre Batistella Bellas Date: Fri, 15 Dec 2023 18:56:27 -0300 Subject: [PATCH] feat: adicionar logisticas - remessas --- README.md | 3 + src/bling.spec.ts | 7 ++ src/bling.ts | 10 ++ .../__tests__/create-response.ts | 15 +++ .../__tests__/delete-response.ts | 1 + .../__tests__/find-response.ts | 56 +++++++++ .../__tests__/get-by-logistic-response.ts | 12 ++ .../__tests__/index.spec.ts | 116 ++++++++++++++++++ .../__tests__/update-response.ts | 11 ++ src/entities/logisticasRemessas/index.ts | 112 +++++++++++++++++ .../interfaces/create.interface.ts | 13 ++ .../interfaces/delete.interface.ts | 6 + .../interfaces/find.interface.ts | 54 ++++++++ .../interfaces/get-by-logistic.interface.ts | 19 +++ .../interfaces/update.interface.ts | 18 +++ .../types/situacao-rastreamento.type.ts | 15 +++ .../logisticasRemessas/types/situacao.type.ts | 15 +++ 17 files changed, 483 insertions(+) create mode 100644 src/entities/logisticasRemessas/__tests__/create-response.ts create mode 100644 src/entities/logisticasRemessas/__tests__/delete-response.ts create mode 100644 src/entities/logisticasRemessas/__tests__/find-response.ts create mode 100644 src/entities/logisticasRemessas/__tests__/get-by-logistic-response.ts create mode 100644 src/entities/logisticasRemessas/__tests__/index.spec.ts create mode 100644 src/entities/logisticasRemessas/__tests__/update-response.ts create mode 100644 src/entities/logisticasRemessas/index.ts create mode 100644 src/entities/logisticasRemessas/interfaces/create.interface.ts create mode 100644 src/entities/logisticasRemessas/interfaces/delete.interface.ts create mode 100644 src/entities/logisticasRemessas/interfaces/find.interface.ts create mode 100644 src/entities/logisticasRemessas/interfaces/get-by-logistic.interface.ts create mode 100644 src/entities/logisticasRemessas/interfaces/update.interface.ts create mode 100644 src/entities/logisticasRemessas/types/situacao-rastreamento.type.ts create mode 100644 src/entities/logisticasRemessas/types/situacao.type.ts diff --git a/README.md b/README.md index fdb40f8..60b1977 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ Disponível para: - [ ] PHP (em breve) - [ ] C# (em breve) +Atualizado com a versão `v291` da API ([veja o registro de alterações](https://developer.bling.com.br/changelogs#2024-01-31)). + **Atenção**: a versão 5.0.0+ do `bling-erp-api` utiliza a API v3 do Bling. Caso deseje utilizar a API v2 do Bling, [utilize a versão 4.0.0](https://github.com/AlexandreBellas/bling-erp-api/tree/v4.0.0). @@ -79,6 +81,7 @@ Todas as entidades do Bling atualmente são permitidas para interação. São el - [x] Logísticas (`.logisticas`) - [x] Logísticas - Etiquetas (`.logisticasEtiquetas`) - [x] Logísticas - Objetos (`.logisticasObjetos`) +- [x] Logísticas - Remessas (`.logisticasRemessas`) - [x] Logísticas - Serviços (`.logisticasServicos`) - [x] Naturezas de Operações (`.naturezasDeOperacoes`) - [x] Notas Fiscais de Consumidor Eletrônicas (`.nfces`) diff --git a/src/bling.spec.ts b/src/bling.spec.ts index 856e380..588e4eb 100644 --- a/src/bling.spec.ts +++ b/src/bling.spec.ts @@ -21,6 +21,7 @@ import { Homologacao } from './entities/homologacao' import { Logisticas } from './entities/logisticas' import { LogisticasEtiquetas } from './entities/logisticasEtiquetas' import { LogisticasObjetos } from './entities/logisticasObjetos' +import { LogisticasRemessas } from './entities/logisticasRemessas' import { LogisticasServicos } from './entities/logisticasServicos' import { NaturezasDeOperacoes } from './entities/naturezasDeOperacoes' import { Nfces } from './entities/nfces' @@ -147,6 +148,12 @@ describe('Bling main module', () => { ) }) + it('should retrieve logísticas - remessas entity', () => { + expect(createBling(chance.word()).logisticasRemessas).toBeInstanceOf( + LogisticasRemessas + ) + }) + it('should retrieve logísticas - serviços entity', () => { expect(createBling(chance.word()).logisticasServicos).toBeInstanceOf( LogisticasServicos diff --git a/src/bling.ts b/src/bling.ts index 6d85184..5d3a053 100644 --- a/src/bling.ts +++ b/src/bling.ts @@ -20,6 +20,7 @@ import { Homologacao } from './entities/homologacao' import { Logisticas } from './entities/logisticas' import { LogisticasEtiquetas } from './entities/logisticasEtiquetas' import { LogisticasObjetos } from './entities/logisticasObjetos' +import { LogisticasRemessas } from './entities/logisticasRemessas' import { LogisticasServicos } from './entities/logisticasServicos' import { NaturezasDeOperacoes } from './entities/naturezasDeOperacoes' import { Nfces } from './entities/nfces' @@ -251,6 +252,15 @@ export default class Bling { return this.getModule(LogisticasObjetos) } + /** + * Obtém a instância de interação com logísticas - remessas. + * + * @return {LogisticasRemessas} + */ + public get logisticasRemessas(): LogisticasRemessas { + return this.getModule(LogisticasRemessas) + } + /** * Obtém a instância de interação com logísticas - serviços. * diff --git a/src/entities/logisticasRemessas/__tests__/create-response.ts b/src/entities/logisticasRemessas/__tests__/create-response.ts new file mode 100644 index 0000000..9d0c79e --- /dev/null +++ b/src/entities/logisticasRemessas/__tests__/create-response.ts @@ -0,0 +1,15 @@ +export default { + data: { + id: 12345678 + } +} + +export const createRequestBody = { + numeroPlp: '749fdc73', + situacao: -3 as const, + descricao: 'Remessa_18092023', + logistica: { + id: 12345678 + }, + objetos: ['12'] +} diff --git a/src/entities/logisticasRemessas/__tests__/delete-response.ts b/src/entities/logisticasRemessas/__tests__/delete-response.ts new file mode 100644 index 0000000..7b85954 --- /dev/null +++ b/src/entities/logisticasRemessas/__tests__/delete-response.ts @@ -0,0 +1 @@ +export default null diff --git a/src/entities/logisticasRemessas/__tests__/find-response.ts b/src/entities/logisticasRemessas/__tests__/find-response.ts new file mode 100644 index 0000000..5385487 --- /dev/null +++ b/src/entities/logisticasRemessas/__tests__/find-response.ts @@ -0,0 +1,56 @@ +export default { + data: { + id: 12345678, + numeroPlp: '749fdc73', + situacao: -3 as const, + descricao: 'Remessa_18092023', + dataCriacao: '2023-09-18', + logistica: { + id: 12345678 + }, + objetos: [ + { + id: 1235456, + remessa: { + id: 12345678 + }, + pedidoVenda: { + id: 12345678 + }, + notaFiscal: { + id: 12345678 + }, + servico: { + id: 12345678, + nome: 'SEDEX 10 A VISTA', + codigo: '04790' + }, + rastreamento: { + codigo: 'EC272330554BR', + descricao: 'Criado', + situacao: 1 as const, + origem: 'São Paulo, SP', + destino: 'São Paulo, SP', + ultimaAlteracao: '2020-11-11 16:40:33', + url: 'https://www.rastreamento.exemplo.com.br/EC272330554BR' + }, + dimensao: { + peso: 1.5, + altura: 1.5, + largura: 1.5, + comprimento: 1.5, + diametro: 1.5 + }, + embalagem: { + id: 12345678 + }, + dataSaida: '2022-12-01', + prazoEntregaPrevisto: 15, + fretePrevisto: 59.9, + valorDeclarado: 55.9, + avisoRecebimento: false, + maoPropria: false + } + ] + } +} diff --git a/src/entities/logisticasRemessas/__tests__/get-by-logistic-response.ts b/src/entities/logisticasRemessas/__tests__/get-by-logistic-response.ts new file mode 100644 index 0000000..302adaf --- /dev/null +++ b/src/entities/logisticasRemessas/__tests__/get-by-logistic-response.ts @@ -0,0 +1,12 @@ +export default { + data: [ + { + id: 12345678, + numeroPlp: '749fdc73', + situacao: -3 as const, + descricao: 'Remessa_18092023', + dataCriacao: '2023-09-18', + objetos: [6423813145] + } + ] +} diff --git a/src/entities/logisticasRemessas/__tests__/index.spec.ts b/src/entities/logisticasRemessas/__tests__/index.spec.ts new file mode 100644 index 0000000..56e4ae4 --- /dev/null +++ b/src/entities/logisticasRemessas/__tests__/index.spec.ts @@ -0,0 +1,116 @@ +import { Chance } from 'chance' +import { LogisticasRemessas } from '..' +import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository' +import { ICreateResponse } from '../interfaces/create.interface' +import { IFindResponse } from '../interfaces/find.interface' +import { IGetByLogisticResponse } from '../interfaces/get-by-logistic.interface' +import { IUpdateResponse } from '../interfaces/update.interface' +import createResponse, { createRequestBody } from './create-response' +import deleteResponse from './delete-response' +import findResponse from './find-response' +import getByLogisticResponse from './get-by-logistic-response' +import updateResponse, { updateRequestBody } from './update-response' + +const chance = Chance() + +describe('Logísticas - Remessas entity', () => { + let repository: InMemoryBlingRepository + let entity: LogisticasRemessas + + beforeEach(() => { + repository = new InMemoryBlingRepository() + entity = new LogisticasRemessas(repository) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('should delete successfully', async () => { + const idRemessa = chance.natural() + const spy = jest.spyOn(repository, 'destroy') + repository.setResponse(deleteResponse) + + const response = await entity.delete({ idRemessa }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'logisticas/remessas', + id: String(idRemessa) + }) + expect(response).toBe(deleteResponse) + + const typingResponseTest: null = deleteResponse + expect(typingResponseTest).toBe(deleteResponse) + }) + + it('should find successfully', async () => { + const spy = jest.spyOn(repository, 'show') + const idRemessa = chance.natural() + repository.setResponse(findResponse) + + const response = await entity.find({ idRemessa }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'logisticas/remessas', + id: String(idRemessa) + }) + expect(response).toBe(findResponse) + + const typingResponseTest: IFindResponse = findResponse + expect(typingResponseTest).toBe(findResponse) + }) + + it('should get by logistic successfully', async () => { + const spy = jest.spyOn(repository, 'show') + const idLogistica = chance.natural() + repository.setResponse(getByLogisticResponse) + + const response = await entity.getByLogistic({ idLogistica }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'logisticas', + id: `${idLogistica}/remessas` + }) + expect(response).toBe(getByLogisticResponse) + + const typingResponseTest: IGetByLogisticResponse = getByLogisticResponse + expect(typingResponseTest).toBe(getByLogisticResponse) + }) + + it('should create successfully', async () => { + const spy = jest.spyOn(repository, 'store') + repository.setResponse(createResponse) + + const response = await entity.create(createRequestBody) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'logisticas/remessas', + body: createRequestBody + }) + expect(response).toBe(createResponse) + + const typingResponseTest: ICreateResponse = createResponse + expect(typingResponseTest).toBe(createResponse) + }) + + it('should update successfully', async () => { + const spy = jest.spyOn(repository, 'replace') + const idRemessa = chance.natural() + repository.setResponse(updateResponse) + + const response = await entity.update({ + idRemessa, + ...updateRequestBody + }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'logisticas/remessas', + id: String(idRemessa), + body: updateRequestBody + }) + expect(response).toBe(updateResponse) + + const typingResponseTest: IUpdateResponse = updateResponse + expect(typingResponseTest).toBe(updateResponse) + }) +}) diff --git a/src/entities/logisticasRemessas/__tests__/update-response.ts b/src/entities/logisticasRemessas/__tests__/update-response.ts new file mode 100644 index 0000000..7e89237 --- /dev/null +++ b/src/entities/logisticasRemessas/__tests__/update-response.ts @@ -0,0 +1,11 @@ +export default { + data: { + id: 12345678 + } +} + +export const updateRequestBody = { + numeroPlp: '749fdc73', + situacao: -3 as const, + descricao: 'Remessa_18092023' +} diff --git a/src/entities/logisticasRemessas/index.ts b/src/entities/logisticasRemessas/index.ts new file mode 100644 index 0000000..12ea8a6 --- /dev/null +++ b/src/entities/logisticasRemessas/index.ts @@ -0,0 +1,112 @@ +import { Entity } from '../@shared/entity' +import { ICreateBody, ICreateResponse } from './interfaces/create.interface' +import { IDeleteParams } from './interfaces/delete.interface' +import { IFindParams, IFindResponse } from './interfaces/find.interface' +import { + IGetByLogisticParams, + IGetByLogisticResponse +} from './interfaces/get-by-logistic.interface' +import { + IUpdateBody, + IUpdateParams, + IUpdateResponse +} from './interfaces/update.interface' + +/** + * Entidade para interação com Logísticas - Remessas. + * + * @see https://developer.bling.com.br/referencia#/Log%C3%ADsticas%20-%20Remessas + */ +export class LogisticasRemessas extends Entity { + /** + * Remove uma remessa de postagem. + * + * @param {IDeleteParams} params Parâmetros da remoção. + * + * @returns {Promise} Não há retorno. + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Log%C3%ADsticas%20-%20Remessas/delete_logisticas_remessas__idRemessa_ + */ + public async delete(params: IDeleteParams): Promise { + return await this.repository.destroy({ + endpoint: 'logisticas/remessas', + id: String(params.idRemessa) + }) + } + + /** + * Obtém uma remessa de postagem. + * + * @param {IFindParams} params Parâmetros da busca. + * + * @returns {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Log%C3%ADsticas%20-%20Remessas/get_logisticas_remessas__idRemessa_ + */ + public async find(params: IFindParams): Promise { + return await this.repository.show({ + endpoint: 'logisticas/remessas', + id: String(params.idRemessa) + }) + } + + /** + * Obtém as remessas de postagem de uma logística. + * + * @param {IGetByLogisticParams} params Parâmetros da busca. + * + * @returns {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Log%C3%ADsticas%20-%20Remessas/get_logisticas__idLogistica__remessas + */ + public async getByLogistic( + params: IGetByLogisticParams + ): Promise { + return await this.repository.show({ + endpoint: 'logisticas', + id: `${params.idLogistica}/remessas` + }) + } + + /** + * Cria uma remessa de postagem de uma logística. + * + * @param {ICreateBody} body O conteúdo para a criação. + * + * @returns {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Log%C3%ADsticas%20-%20Remessas/post_logisticas_remessas + */ + public async create(body: ICreateBody): Promise { + return await this.repository.store({ + endpoint: 'logisticas/remessas', + body + }) + } + + /** + * Altera uma remessa de postagem. + * + * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização. + * + * @return {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Log%C3%ADsticas%20-%20Remessas/put_logisticas_remessas__idRemessa_ + */ + public async update( + params: IUpdateParams & IUpdateBody + ): Promise { + const { idRemessa, ...body } = params + + return await this.repository.replace({ + endpoint: 'logisticas/remessas', + id: String(idRemessa), + body + }) + } +} diff --git a/src/entities/logisticasRemessas/interfaces/create.interface.ts b/src/entities/logisticasRemessas/interfaces/create.interface.ts new file mode 100644 index 0000000..d74d8f9 --- /dev/null +++ b/src/entities/logisticasRemessas/interfaces/create.interface.ts @@ -0,0 +1,13 @@ +import { ISituacao } from '../types/situacao.type' + +export interface ICreateBody { + numeroPlp: string + situacao: ISituacao + descricao: string + logistica?: { id: number } + objetos: string[] +} + +export interface ICreateResponse { + data: { id: number } +} diff --git a/src/entities/logisticasRemessas/interfaces/delete.interface.ts b/src/entities/logisticasRemessas/interfaces/delete.interface.ts new file mode 100644 index 0000000..e6cdff7 --- /dev/null +++ b/src/entities/logisticasRemessas/interfaces/delete.interface.ts @@ -0,0 +1,6 @@ +export interface IDeleteParams { + /** + * ID da remessa de postagem + */ + idRemessa: number +} diff --git a/src/entities/logisticasRemessas/interfaces/find.interface.ts b/src/entities/logisticasRemessas/interfaces/find.interface.ts new file mode 100644 index 0000000..7f2513c --- /dev/null +++ b/src/entities/logisticasRemessas/interfaces/find.interface.ts @@ -0,0 +1,54 @@ +import { ISituacaoRastreamento } from '../types/situacao-rastreamento.type' +import { ISituacao } from '../types/situacao.type' + +export interface IFindParams { + /** + * ID da remessa de postagem + */ + idRemessa: number +} + +export interface IFindResponse { + data: { + id: number + numeroPlp: string + situacao: ISituacao + descricao: string + dataCriacao: string + logistica: { id: number } + objetos: { + id: number + remessa?: { id: number } + pedidoVenda: { id: number } + notaFiscal: { id: number } + servico: { + id: number + nome: string + codigo: string + } + rastreamento: { + codigo: string + descricao: string + situacao: ISituacaoRastreamento + origem: string + destino: string + ultimaAlteracao: string + url: string + } + dimensao: { + peso: number + altura: number + largura: number + comprimento: number + diametro: number + } + embalagem: { id: number } + dataSaida: string + prazoEntregaPrevisto: number + fretePrevisto: number + valorDeclarado: number + avisoRecebimento: boolean + maoPropria: boolean + }[] + } +} diff --git a/src/entities/logisticasRemessas/interfaces/get-by-logistic.interface.ts b/src/entities/logisticasRemessas/interfaces/get-by-logistic.interface.ts new file mode 100644 index 0000000..c028acd --- /dev/null +++ b/src/entities/logisticasRemessas/interfaces/get-by-logistic.interface.ts @@ -0,0 +1,19 @@ +import { ISituacao } from '../types/situacao.type' + +export interface IGetByLogisticParams { + /** + * ID da logística + */ + idLogistica: number +} + +export interface IGetByLogisticResponse { + data: { + id: number + numeroPlp: string + situacao: ISituacao + descricao: string + dataCriacao: string + objetos: number[] + }[] +} diff --git a/src/entities/logisticasRemessas/interfaces/update.interface.ts b/src/entities/logisticasRemessas/interfaces/update.interface.ts new file mode 100644 index 0000000..9e4053b --- /dev/null +++ b/src/entities/logisticasRemessas/interfaces/update.interface.ts @@ -0,0 +1,18 @@ +import { ISituacao } from '../types/situacao.type' + +export interface IUpdateParams { + /** + * ID da remessa de postagem + */ + idRemessa: number +} + +export interface IUpdateBody { + numeroPlp: string + situacao: ISituacao + descricao: string +} + +export interface IUpdateResponse { + data: { id: number } +} diff --git a/src/entities/logisticasRemessas/types/situacao-rastreamento.type.ts b/src/entities/logisticasRemessas/types/situacao-rastreamento.type.ts new file mode 100644 index 0000000..d04be41 --- /dev/null +++ b/src/entities/logisticasRemessas/types/situacao-rastreamento.type.ts @@ -0,0 +1,15 @@ +/** + * Tipagem referente à situação do rastreamento. + * + * - `0`: Postado + * - `1`: Em andamento + * - `2`: Não entregue + * - `3`: Entregue + * - `4`: Aguardando retirada + * - `5`: Em aberto + * - `6`: Vinculado + * - `7`: Atrasado + * - `8`: Não postado + * - `9`: Entrega suspensa + */ +export type ISituacaoRastreamento = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 diff --git a/src/entities/logisticasRemessas/types/situacao.type.ts b/src/entities/logisticasRemessas/types/situacao.type.ts new file mode 100644 index 0000000..167242d --- /dev/null +++ b/src/entities/logisticasRemessas/types/situacao.type.ts @@ -0,0 +1,15 @@ +/** + * Tipagem referente à situação de uma remessa de uma logística. + * + * - `-3`: A ser corrigida + * - `-2`: Em processamento + * - `-1`: Cancelado + * - `0`: Em aberto + * - `1`: Emitido + * - `2`: Pronto para envio + * - `3`: Despachado + * - `4`: Pronto para envio + * - `5`: Etiqueta comprada + * - `6`: Etiqueta parcialmente comprada + */ +export type ISituacao = -3 | -2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6