diff --git a/.vscode/settings.json b/.vscode/settings.json index aefbb7d..3abad4f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "exceptions": "Error" }, "cSpell.words": [ + "agrupadores", "aliquota", "Amazônia", "borderô", 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/contatos/__tests__/find-response.ts b/src/entities/contatos/__tests__/find-response.ts index 6e6e3da..7de80e9 100644 --- a/src/entities/contatos/__tests__/find-response.ts +++ b/src/entities/contatos/__tests__/find-response.ts @@ -3,13 +3,13 @@ export default { id: 12345678, nome: 'Contato', codigo: 'ASD001', - situacao: 'A', + situacao: 'A' as const, numeroDocumento: '12345678910', telefone: '(54) 3333-4444', celular: '(54) 99999-8888', fantasia: 'Nome fantasia', - tipo: 'J', - indicadorIe: 1, + tipo: 'J' as const, + indicadorIe: 1 as const, ie: '123.456.789.101', rg: '1234567890', orgaoEmissor: '1234567890', @@ -20,7 +20,7 @@ export default { cep: '95702-000', bairro: 'Imigrante', municipio: 'Bento Gonçalves', - uf: 'RS', + uf: 'RS' as const, numero: '914', complemento: 'Sede 101' }, @@ -29,7 +29,7 @@ export default { cep: '95702-000', bairro: 'Imigrante', municipio: 'Bento Gonçalves', - uf: 'RS', + uf: 'RS' as const, numero: '914', complemento: 'Sede 101' } @@ -39,11 +39,11 @@ export default { }, dadosAdicionais: { dataNascimento: '1990-08-24', - sexo: 'M', + sexo: 'M' as const, naturalidade: 'Brasileira' }, financeiro: { - limiteCredito: 0, + limiteCredito: 0 as const, condicaoPagamento: '30', categoria: { id: 12345678 diff --git a/src/entities/contatos/__tests__/get-response.ts b/src/entities/contatos/__tests__/get-response.ts index cd43b28..9aa9f71 100644 --- a/src/entities/contatos/__tests__/get-response.ts +++ b/src/entities/contatos/__tests__/get-response.ts @@ -4,7 +4,7 @@ export default { id: 12345678, nome: 'Contato', codigo: 'ASD001', - situacao: 'A', + situacao: 'A' as const, numeroDocumento: '123.456.789-10', telefone: '(54) 3333-4444', celular: '(54) 99999-8888' diff --git a/src/entities/contatos/__tests__/index.spec.ts b/src/entities/contatos/__tests__/index.spec.ts index 42d896b..6594cfe 100644 --- a/src/entities/contatos/__tests__/index.spec.ts +++ b/src/entities/contatos/__tests__/index.spec.ts @@ -1,6 +1,10 @@ import { Chance } from 'chance' import { Contatos } from '..' import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository' +import { ICreateResponse } from '../interfaces/create.interface' +import { IFindTypesResponse } from '../interfaces/find-types.interface' +import { IFindResponse } from '../interfaces/find.interface' +import { IGetResponse } from '../interfaces/get.interface' import changeSituationManyResponse, { changeSituationManyRequest } from './change-situation-many-response' @@ -46,6 +50,9 @@ describe('Contatos entity', () => { params: { idsContatos } }) expect(response).toBe(deleteManyResponse) + + const typingResponseTest: null = deleteManyResponse + expect(typingResponseTest).toBe(deleteManyResponse) }) it('should delete successfully', async () => { @@ -60,6 +67,9 @@ describe('Contatos entity', () => { id: String(idContato) }) expect(response).toBe(deleteResponse) + + const typingResponseTest: null = deleteResponse + expect(typingResponseTest).toBe(deleteResponse) }) it('should get successfully', async () => { @@ -75,6 +85,10 @@ describe('Contatos entity', () => { pagina: undefined, pesquisa: undefined, criterio: undefined, + dataInclusaoInicial: undefined, + dataInclusaoFinal: undefined, + dataAlteracaoInicial: undefined, + dataAlteracaoFinal: undefined, idTipoContato: undefined, idVendedor: undefined, uf: undefined, @@ -84,6 +98,9 @@ describe('Contatos entity', () => { } }) expect(response).toBe(getResponse) + + const typingResponseTest: IGetResponse = getResponse + expect(typingResponseTest).toBe(getResponse) }) it('should find successfully', async () => { @@ -98,6 +115,9 @@ describe('Contatos entity', () => { id: String(idContato) }) expect(response).toBe(findResponse) + + const typingResponseTest: IFindResponse = findResponse + expect(typingResponseTest).toBe(findResponse) }) it('should find types successfully', async () => { @@ -112,6 +132,9 @@ describe('Contatos entity', () => { id: `${idContato}/tipos` }) expect(response).toBe(findTypesResponse) + + const typingResponseTest: IFindTypesResponse = findTypesResponse + expect(typingResponseTest).toBe(findTypesResponse) }) it('should change situation successfully', async () => { @@ -130,6 +153,9 @@ describe('Contatos entity', () => { body: changeSituationRequest }) expect(response).toBe(changeSituationResponse) + + const typingResponseTest: null = changeSituationResponse + expect(typingResponseTest).toBe(changeSituationResponse) }) it('should change situation many successfully', async () => { @@ -144,7 +170,10 @@ describe('Contatos entity', () => { endpoint: 'contatos/situacoes', body: changeSituationManyRequest }) - expect(response).toBe(changeSituationResponse) + expect(response).toBe(changeSituationManyResponse) + + const typingResponseTest: null = changeSituationManyResponse + expect(typingResponseTest).toBe(changeSituationManyResponse) }) it('should create successfully', async () => { @@ -158,6 +187,9 @@ describe('Contatos entity', () => { body: createRequestBody }) expect(response).toBe(createResponse) + + const typingResponseTest: ICreateResponse = createResponse + expect(typingResponseTest).toBe(createResponse) }) it('should update successfully', async () => { @@ -176,5 +208,8 @@ describe('Contatos entity', () => { body: updateRequestBody }) expect(response).toBe(updateResponse) + + const typingResponseTest: null = updateResponse + expect(typingResponseTest).toBe(updateResponse) }) }) diff --git a/src/entities/contatos/index.ts b/src/entities/contatos/index.ts index 885f3b3..3700a6b 100644 --- a/src/entities/contatos/index.ts +++ b/src/entities/contatos/index.ts @@ -76,6 +76,18 @@ export class Contatos extends Entity { limite: params?.limite, pesquisa: params?.pesquisa, criterio: params?.criterio, + dataInclusaoInicial: this.prepareStringOrDateParam( + params?.dataInclusaoInicial + ), + dataInclusaoFinal: this.prepareStringOrDateParam( + params?.dataInclusaoFinal + ), + dataAlteracaoInicial: this.prepareStringOrDateParam( + params?.dataAlteracaoInicial + ), + dataAlteracaoFinal: this.prepareStringOrDateParam( + params?.dataAlteracaoFinal + ), idTipoContato: params?.idTipoContato, idVendedor: params?.idVendedor, uf: params?.uf, diff --git a/src/entities/contatos/interfaces/get.interface.ts b/src/entities/contatos/interfaces/get.interface.ts index 8260a4c..7beb4f0 100644 --- a/src/entities/contatos/interfaces/get.interface.ts +++ b/src/entities/contatos/interfaces/get.interface.ts @@ -19,6 +19,22 @@ export interface IGetParams { * Criterio de listagem */ criterio?: ICriterio + /** + * Data de inclusão inicial + */ + dataInclusaoInicial?: Date | string + /** + * Data de inclusão final + */ + dataInclusaoFinal?: Date | string + /** + * Data de alteração inicial + */ + dataAlteracaoInicial?: Date | string + /** + * Data de alteração final + */ + dataAlteracaoFinal?: Date | string /** * ID do tipo do contato */ diff --git a/src/entities/logisticasEtiquetas/__tests__/index.spec.ts b/src/entities/logisticasEtiquetas/__tests__/index.spec.ts index cb3154c..9abc517 100644 --- a/src/entities/logisticasEtiquetas/__tests__/index.spec.ts +++ b/src/entities/logisticasEtiquetas/__tests__/index.spec.ts @@ -1,6 +1,7 @@ import { Chance } from 'chance' import { LogisticasEtiquetas } from '..' import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository' +import { IFormato } from '../types/formato.type' import getResponse from './get-response' const chance = Chance() @@ -21,17 +22,17 @@ describe('Logísticas - Etiquetas entity', () => { it('should get successfully', async () => { const spy = jest.spyOn(repository, 'index') repository.setResponse(getResponse) + const formato = chance.pickone(['PDF', 'ZPL']) as IFormato const idsVendas: number[] = [] for (let i = 0; i < chance.natural({ min: 1, max: 5 }) + 1; i++) { idsVendas.push(chance.natural()) } - const response = await entity.get({ idsVendas }) + const response = await entity.get({ formato, idsVendas }) expect(spy).toHaveBeenCalledWith({ endpoint: 'logisticas/etiquetas', - body: { idsVendas }, - params: { formato: undefined } + params: { formato, idsVendas } }) expect(response).toBe(getResponse) }) diff --git a/src/entities/logisticasEtiquetas/index.ts b/src/entities/logisticasEtiquetas/index.ts index 7f1e0f2..b7bd330 100644 --- a/src/entities/logisticasEtiquetas/index.ts +++ b/src/entities/logisticasEtiquetas/index.ts @@ -1,5 +1,5 @@ import { Entity } from '../@shared/entity' -import { IGetBody, IGetParams, IGetResponse } from './interfaces/get.interface' +import { IGetParams, IGetResponse } from './interfaces/get.interface' /** * Entidade para interação com logísticas - etiquetas. @@ -10,19 +10,20 @@ export class LogisticasEtiquetas extends Entity { /** * Obtém etiquetas das vendas. * - * @param {IGetParams & IGetBody} params Parâmetros da busca. + * @param {IGetParams} params Parâmetros da busca. * * @returns {Promise} * @throws {BlingApiException|BlingInternalException} * * @see https://developer.bling.com.br/referencia#/Log%C3%ADsticas%20-%20Etiquetas/get_logisticas_etiquetas */ - public async get(params: IGetParams & IGetBody): Promise { - const { formato, ...body } = params + public async get(params: IGetParams): Promise { return await this.repository.index({ endpoint: 'logisticas/etiquetas', - body, - params: { formato } + params: { + formato: params.formato, + idsVendas: params.idsVendas + } }) } } diff --git a/src/entities/logisticasEtiquetas/interfaces/get.interface.ts b/src/entities/logisticasEtiquetas/interfaces/get.interface.ts index 900c9e7..b160c63 100644 --- a/src/entities/logisticasEtiquetas/interfaces/get.interface.ts +++ b/src/entities/logisticasEtiquetas/interfaces/get.interface.ts @@ -1,10 +1,7 @@ import { IFormato } from '../types/formato.type' export interface IGetParams { - formato?: IFormato -} - -export interface IGetBody { + formato: IFormato idsVendas: number[] } 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 diff --git a/src/entities/naturezasDeOperacoes/__tests__/get-response.ts b/src/entities/naturezasDeOperacoes/__tests__/get-response.ts index 0926f91..8b475de 100644 --- a/src/entities/naturezasDeOperacoes/__tests__/get-response.ts +++ b/src/entities/naturezasDeOperacoes/__tests__/get-response.ts @@ -2,8 +2,8 @@ export default { data: [ { id: 12345678, - situacao: 1, - padrao: 1, + situacao: 1 as const, + padrao: 1 as const, descricao: 'Compra de Mercadoria' } ] diff --git a/src/entities/naturezasDeOperacoes/__tests__/index.spec.ts b/src/entities/naturezasDeOperacoes/__tests__/index.spec.ts index 52bdeab..478d472 100644 --- a/src/entities/naturezasDeOperacoes/__tests__/index.spec.ts +++ b/src/entities/naturezasDeOperacoes/__tests__/index.spec.ts @@ -1,6 +1,8 @@ import { Chance } from 'chance' import { NaturezasDeOperacoes } from '..' import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository' +import { ICalculateItemTaxResponse } from '../interfaces/calculate-item-tax.interface' +import { IGetResponse } from '../interfaces/get.interface' import calculateItemTaxResponse, { calculateItemTaxRequestBody } from './calculate-item-tax-response' @@ -37,6 +39,9 @@ describe('Naturezas de Operação entity', () => { } }) expect(response).toBe(getResponse) + + const typingResponseTest: IGetResponse = getResponse + expect(typingResponseTest).toBe(getResponse) }) it('should calculate item tax successfully', async () => { @@ -54,5 +59,9 @@ describe('Naturezas de Operação entity', () => { body: calculateItemTaxRequestBody }) expect(response).toBe(calculateItemTaxResponse) + + const typingResponseTest: ICalculateItemTaxResponse = + calculateItemTaxResponse + expect(typingResponseTest).toBe(calculateItemTaxResponse) }) }) diff --git a/src/entities/pedidosVendas/__tests__/create-response.ts b/src/entities/pedidosVendas/__tests__/create-response.ts index 8b3b58a..329646f 100644 --- a/src/entities/pedidosVendas/__tests__/create-response.ts +++ b/src/entities/pedidosVendas/__tests__/create-response.ts @@ -118,5 +118,10 @@ export const createRequestBody = { intermediador: { cnpj: '13921649000197', nomeUsuario: 'usuario' + }, + taxas: { + taxaComissao: 1, + custoFrete: 9.99, + valorBase: 129.9 } } diff --git a/src/entities/pedidosVendas/__tests__/find-response.ts b/src/entities/pedidosVendas/__tests__/find-response.ts index 3e28409..b8608e3 100644 --- a/src/entities/pedidosVendas/__tests__/find-response.ts +++ b/src/entities/pedidosVendas/__tests__/find-response.ts @@ -16,7 +16,7 @@ export default { }, situacao: { id: 12345678, - valor: 1 as const + valor: 1 }, loja: { id: 12345678 @@ -27,7 +27,7 @@ export default { observacoesInternas: 'Observações internas do pedido.', desconto: { valor: 15.45, - unidade: 'REAL' + unidade: 'REAL' as const }, categoria: { id: 12345678 @@ -106,6 +106,11 @@ export default { intermediador: { cnpj: '13921649000197', nomeUsuario: 'usuario' + }, + taxas: { + taxaComissao: 1, + custoFrete: 9.99, + valorBase: 129.9 } } } diff --git a/src/entities/pedidosVendas/__tests__/generate-nfce-response.ts b/src/entities/pedidosVendas/__tests__/generate-nfce-response.ts new file mode 100644 index 0000000..b054799 --- /dev/null +++ b/src/entities/pedidosVendas/__tests__/generate-nfce-response.ts @@ -0,0 +1,3 @@ +export default { + idNotaFiscal: 12345678 +} diff --git a/src/entities/pedidosVendas/__tests__/generate-nfe-response.ts b/src/entities/pedidosVendas/__tests__/generate-nfe-response.ts new file mode 100644 index 0000000..b054799 --- /dev/null +++ b/src/entities/pedidosVendas/__tests__/generate-nfe-response.ts @@ -0,0 +1,3 @@ +export default { + idNotaFiscal: 12345678 +} diff --git a/src/entities/pedidosVendas/__tests__/index.spec.ts b/src/entities/pedidosVendas/__tests__/index.spec.ts index ed37a20..5c3aaec 100644 --- a/src/entities/pedidosVendas/__tests__/index.spec.ts +++ b/src/entities/pedidosVendas/__tests__/index.spec.ts @@ -1,11 +1,20 @@ import { Chance } from 'chance' import { PedidosVendas } from '..' import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository' +import { ICreateResponse } from '../interfaces/create.interface' +import { IDeleteManyResponse } from '../interfaces/delete-many.interface' +import { IFindResponse } from '../interfaces/find.interface' +import { IGenerateNfceResponse } from '../interfaces/generate-nfce.interface' +import { IGenerateNfeResponse } from '../interfaces/generate-nfe.interface' +import { IGetResponse } from '../interfaces/get.interface' +import { IUpdateResponse } from '../interfaces/update.interface' import changeSituationResponse from './change-situation-response' import createResponse, { createRequestBody } from './create-response' import deleteManyResponse from './delete-many-response' import deleteResponse from './delete-response' import findResponse from './find-response' +import generateNfceResponse from './generate-nfce-response' +import generateNfeResponse from './generate-nfe-response' import getResponse from './get-response' import postAccountsResponse from './post-accounts-response' import postStockResponse from './post-stock-response' @@ -45,6 +54,9 @@ describe('Pedidos - Vendas entity', () => { params: { idsPedidosVendas } }) expect(response).toBe(deleteManyResponse) + + const typingResponseTest: IDeleteManyResponse = deleteManyResponse + expect(typingResponseTest).toBe(deleteManyResponse) }) it('should delete successfully', async () => { @@ -59,6 +71,9 @@ describe('Pedidos - Vendas entity', () => { id: String(idPedidoVenda) }) expect(response).toBe(deleteResponse) + + const typingResponseTest: null = deleteResponse + expect(typingResponseTest).toBe(deleteResponse) }) it('should get successfully', async () => { @@ -88,6 +103,9 @@ describe('Pedidos - Vendas entity', () => { } }) expect(response).toBe(getResponse) + + const typingResponseTest: IGetResponse = getResponse + expect(typingResponseTest).toBe(getResponse) }) it('should find successfully', async () => { @@ -102,6 +120,9 @@ describe('Pedidos - Vendas entity', () => { id: String(idPedidoVenda) }) expect(response).toBe(findResponse) + + const typingResponseTest: IFindResponse = findResponse + expect(typingResponseTest).toBe(findResponse) }) it('should change situation successfully', async () => { @@ -118,6 +139,9 @@ describe('Pedidos - Vendas entity', () => { body: {} }) expect(response).toBe(changeSituationResponse) + + const typingResponseTest: null = changeSituationResponse + expect(typingResponseTest).toBe(changeSituationResponse) }) it('should post stock to deposit successfully', async () => { @@ -136,6 +160,9 @@ describe('Pedidos - Vendas entity', () => { body: {} }) expect(response).toBe(postStockToDepositResponse) + + const typingResponseTest: null = postStockToDepositResponse + expect(typingResponseTest).toBe(postStockToDepositResponse) }) it('should post stock successfully', async () => { @@ -150,6 +177,9 @@ describe('Pedidos - Vendas entity', () => { body: {} }) expect(response).toBe(postStockResponse) + + const typingResponseTest: null = postStockResponse + expect(typingResponseTest).toBe(postStockResponse) }) it('should reverse stock successfully', async () => { @@ -164,6 +194,9 @@ describe('Pedidos - Vendas entity', () => { body: {} }) expect(response).toBe(reverseStockResponse) + + const typingResponseTest: null = reverseStockResponse + expect(typingResponseTest).toBe(reverseStockResponse) }) it('should post accounts successfully', async () => { @@ -178,6 +211,9 @@ describe('Pedidos - Vendas entity', () => { body: {} }) expect(response).toBe(postAccountsResponse) + + const typingResponseTest: null = postAccountsResponse + expect(typingResponseTest).toBe(postAccountsResponse) }) it('should reverse accounts successfully', async () => { @@ -192,6 +228,43 @@ describe('Pedidos - Vendas entity', () => { body: {} }) expect(response).toBe(reverseAccountsResponse) + + const typingResponseTest: null = reverseAccountsResponse + expect(typingResponseTest).toBe(reverseAccountsResponse) + }) + + it('should generate nfe successfully', async () => { + const spy = jest.spyOn(repository, 'store') + const idPedidoVenda = chance.natural() + repository.setResponse(generateNfeResponse) + + const response = await entity.generateNfe({ idPedidoVenda }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: `pedidos/vendas/${idPedidoVenda}/gerar-nfe`, + body: {} + }) + expect(response).toBe(generateNfeResponse) + + const typingResponseTest: IGenerateNfeResponse = generateNfeResponse + expect(typingResponseTest).toBe(generateNfeResponse) + }) + + it('should generate nfce successfully', async () => { + const spy = jest.spyOn(repository, 'store') + const idPedidoVenda = chance.natural() + repository.setResponse(generateNfceResponse) + + const response = await entity.generateNfce({ idPedidoVenda }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: `pedidos/vendas/${idPedidoVenda}/gerar-nfce`, + body: {} + }) + expect(response).toBe(generateNfceResponse) + + const typingResponseTest: IGenerateNfceResponse = generateNfceResponse + expect(typingResponseTest).toBe(generateNfceResponse) }) it('should create successfully', async () => { @@ -205,6 +278,9 @@ describe('Pedidos - Vendas entity', () => { body: createRequestBody }) expect(response).toBe(createResponse) + + const typingResponseTest: ICreateResponse = createResponse + expect(typingResponseTest).toBe(createResponse) }) it('should update successfully', async () => { @@ -223,5 +299,8 @@ describe('Pedidos - Vendas entity', () => { body: updateRequestBody }) expect(response).toBe(updateResponse) + + const typingResponseTest: IUpdateResponse = updateResponse + expect(typingResponseTest).toBe(updateResponse) }) }) diff --git a/src/entities/pedidosVendas/__tests__/update-response.ts b/src/entities/pedidosVendas/__tests__/update-response.ts index 029115b..6a7318c 100644 --- a/src/entities/pedidosVendas/__tests__/update-response.ts +++ b/src/entities/pedidosVendas/__tests__/update-response.ts @@ -118,5 +118,10 @@ export const updateRequestBody = { intermediador: { cnpj: '13921649000197', nomeUsuario: 'usuario' + }, + taxas: { + taxaComissao: 1, + custoFrete: 9.99, + valorBase: 129.9 } } diff --git a/src/entities/pedidosVendas/index.ts b/src/entities/pedidosVendas/index.ts index b660550..8eef756 100644 --- a/src/entities/pedidosVendas/index.ts +++ b/src/entities/pedidosVendas/index.ts @@ -7,6 +7,14 @@ import { } from './interfaces/delete-many.interface' import { IDeleteParams } from './interfaces/delete.interface' import { IFindParams, IFindResponse } from './interfaces/find.interface' +import { + IGenerateNfceParams, + IGenerateNfceResponse +} from './interfaces/generate-nfce.interface' +import { + IGenerateNfeParams, + IGenerateNfeResponse +} from './interfaces/generate-nfe.interface' import { IGetParams, IGetResponse } from './interfaces/get.interface' import { IPostAccountsParams } from './interfaces/post-accounts.interface' import { IPostStockToDepositParams } from './interfaces/post-stock-to-deposit.interface' @@ -242,6 +250,44 @@ export class PedidosVendas extends Entity { }) } + /** + * Gera nota fiscal eletrônica a partir do pedido de venda. + * + * @param {IGenerateNfeParams} params O conteúdo para o lançamento. + * + * @returns {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Pedidos%20-%20Vendas/post_pedidos_vendas__idPedidoVenda__gerar_nfe + */ + public async generateNfe( + params: IGenerateNfeParams + ): Promise { + return await this.repository.store({ + endpoint: `pedidos/vendas/${params.idPedidoVenda}/gerar-nfe`, + body: {} + }) + } + + /** + * Gera nota fiscal de consumidor eletrônica a partir do pedido de venda. + * + * @param {IGenerateNfceParams} params O conteúdo para o lançamento. + * + * @returns {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Pedidos%20-%20Vendas/post_pedidos_vendas__idPedidoVenda__gerar_nfce + */ + public async generateNfce( + params: IGenerateNfceParams + ): Promise { + return await this.repository.store({ + endpoint: `pedidos/vendas/${params.idPedidoVenda}/gerar-nfce`, + body: {} + }) + } + /** * Altera um pedido de venda. * diff --git a/src/entities/pedidosVendas/interfaces/create.interface.ts b/src/entities/pedidosVendas/interfaces/create.interface.ts index ef9e31f..ab56f9a 100644 --- a/src/entities/pedidosVendas/interfaces/create.interface.ts +++ b/src/entities/pedidosVendas/interfaces/create.interface.ts @@ -85,6 +85,11 @@ export interface ICreateBody { cnpj?: string nomeUsuario?: string } + taxas?: { + taxaComissao?: number + custoFrete?: number + valorBase?: number + } } export interface ICreateResponse { diff --git a/src/entities/pedidosVendas/interfaces/find.interface.ts b/src/entities/pedidosVendas/interfaces/find.interface.ts index fde5baa..40efdd4 100644 --- a/src/entities/pedidosVendas/interfaces/find.interface.ts +++ b/src/entities/pedidosVendas/interfaces/find.interface.ts @@ -102,5 +102,10 @@ export interface IFindResponse { cnpj?: string nomeUsuario?: string } + taxas?: { + taxaComissao?: number + custoFrete?: number + valorBase?: number + } } } diff --git a/src/entities/pedidosVendas/interfaces/generate-nfce.interface.ts b/src/entities/pedidosVendas/interfaces/generate-nfce.interface.ts new file mode 100644 index 0000000..fb43f52 --- /dev/null +++ b/src/entities/pedidosVendas/interfaces/generate-nfce.interface.ts @@ -0,0 +1,10 @@ +export interface IGenerateNfceParams { + /** + * ID do pedido de venda + */ + idPedidoVenda: number +} + +export interface IGenerateNfceResponse { + idNotaFiscal: number +} diff --git a/src/entities/pedidosVendas/interfaces/generate-nfe.interface.ts b/src/entities/pedidosVendas/interfaces/generate-nfe.interface.ts new file mode 100644 index 0000000..37677f7 --- /dev/null +++ b/src/entities/pedidosVendas/interfaces/generate-nfe.interface.ts @@ -0,0 +1,10 @@ +export interface IGenerateNfeParams { + /** + * ID do pedido de venda + */ + idPedidoVenda: number +} + +export interface IGenerateNfeResponse { + idNotaFiscal: number +} diff --git a/src/entities/pedidosVendas/interfaces/get.interface.ts b/src/entities/pedidosVendas/interfaces/get.interface.ts index 72999c9..74d53b7 100644 --- a/src/entities/pedidosVendas/interfaces/get.interface.ts +++ b/src/entities/pedidosVendas/interfaces/get.interface.ts @@ -64,29 +64,27 @@ export interface IGetParams { } export interface IGetResponse { - data: [ - { - id?: number - numero?: number - numeroLoja?: string - data: string - dataSaida: string - dataPrevista: string - totalProdutos?: number - total?: number - contato: { - id: number - nome: string - tipoPessoa?: ITipoPessoa - numeroDocumento?: string - } - situacao?: { - id: number - valor: number - } - loja?: { - id: number - } + data: { + id?: number + numero?: number + numeroLoja?: string + data: string + dataSaida: string + dataPrevista: string + totalProdutos?: number + total?: number + contato: { + id: number + nome: string + tipoPessoa?: ITipoPessoa + numeroDocumento?: string } - ] + situacao?: { + id: number + valor: number + } + loja?: { + id: number + } + }[] } diff --git a/src/entities/pedidosVendas/interfaces/update.interface.ts b/src/entities/pedidosVendas/interfaces/update.interface.ts index f8e9fc8..1f00a23 100644 --- a/src/entities/pedidosVendas/interfaces/update.interface.ts +++ b/src/entities/pedidosVendas/interfaces/update.interface.ts @@ -92,6 +92,11 @@ export interface IUpdateBody { cnpj?: string nomeUsuario?: string } + taxas?: { + taxaComissao?: number + custoFrete?: number + valorBase?: number + } } export interface IUpdateResponse {