diff --git a/README.md b/README.md index a8797f3..66445d6 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Pacote de integração com a [API v3 do ERP Bling](https://developer.bling.com.br) para Javascript/TypeScript. O mais completo existente. -Atualizado com a versão `v304` da API ([veja o registro de alterações](https://developer.bling.com.br/changelogs#2024-07-04)). +Atualizado com a versão `v305` da API ([veja o registro de alterações](https://developer.bling.com.br/changelogs#2024-07-04)). **Atenção**: a versão 5.0.0+ do `bling-erp-api` para Javascript/TypeScript utiliza a API v3 do Bling. Caso deseja utilizar a API v2 do Bling, @@ -44,7 +44,7 @@ projeto de demonstração](https://github.com/AlexandreBellas/bling-erp-api-js/t ## Entidades disponíveis -Quase todas as entidades do Bling atualmente são permitidas para interação. São elas: +Todas as entidades do Bling atualmente são permitidas para interação. São elas: - [x] Borderos (`.borderos`) - [x] Campos customizados (`.camposCustomizados`) @@ -62,7 +62,7 @@ Quase todas as entidades do Bling atualmente são permitidas para interação. S - [x] Empresas (`.empresas`) - [x] Estoques (`.estoques`) - [x] Formas de Pagamento (`.formasDePagamento`) -- [ ] Grupos de Produtos (`.gruposDeProdutos`) +- [x] Grupos de Produtos (`.gruposDeProdutos`) - [x] Homologação (`.homologacao`) - [x] Logísticas (`.logisticas`) - [x] Logísticas - Etiquetas (`.logisticasEtiquetas`) diff --git a/src/bling.spec.ts b/src/bling.spec.ts index f8ee341..9e25578 100644 --- a/src/bling.spec.ts +++ b/src/bling.spec.ts @@ -43,6 +43,7 @@ import { Vendedores } from './entities/vendedores' import { CanaisDeVenda } from './entities/canaisDeVenda' import { OrdensDeProducao } from './entities/ordensDeProducao' import { PropostasComerciais } from './entities/propostasComerciais' +import { GruposDeProdutos } from './entities/gruposDeProdutos' const chance = Chance() @@ -266,4 +267,10 @@ describe('Bling main module', () => { PropostasComerciais ) }) + + it('should retrieve grupos de produtos entity', () => { + expect(createBling(chance.word()).gruposDeProdutos).toBeInstanceOf( + GruposDeProdutos + ) + }) }) diff --git a/src/bling.ts b/src/bling.ts index bc31801..ec6f59f 100644 --- a/src/bling.ts +++ b/src/bling.ts @@ -17,6 +17,7 @@ import { Depositos } from './entities/depositos' import { Empresas } from './entities/empresas' import { Estoques } from './entities/estoques' import { FormasDePagamento } from './entities/formasDePagamento' +import { GruposDeProdutos } from './entities/gruposDeProdutos' import { Homologacao } from './entities/homologacao' import { Logisticas } from './entities/logisticas' import { LogisticasEtiquetas } from './entities/logisticasEtiquetas' @@ -452,4 +453,13 @@ export default class Bling { public get propostasComerciais(): PropostasComerciais { return this.getModule(PropostasComerciais) } + + /** + * Obtém a instância de interação com grupos de produtos. + * + * @return {GruposDeProdutos} + */ + public get gruposDeProdutos(): GruposDeProdutos { + return this.getModule(GruposDeProdutos) + } } diff --git a/src/entities/contasReceber/interfaces/get.interface.ts b/src/entities/contasReceber/interfaces/get.interface.ts index 3e2fb2b..60f2c81 100644 --- a/src/entities/contasReceber/interfaces/get.interface.ts +++ b/src/entities/contasReceber/interfaces/get.interface.ts @@ -7,14 +7,16 @@ export interface IGetParams { limite?: number situacoes?: ISituacao[] /** - * `E`: filtrar por data de emissão - * `V`: filtrar por data de vencimento + * - `E`: filtrar por data de emissão + * - `V`: filtrar por data de vencimento + * - `R`: Data de recebimento */ - tipoFiltroData?: 'E' | 'V' + tipoFiltroData?: 'E' | 'V' | 'R' dataInicial?: Date | string dataFinal?: Date | string idsCategorias?: number[] idPortador?: number + idContato?: number idVendedor?: number idFormaPagamento?: number boletoGerado?: number diff --git a/src/entities/formasDePagamento/types/tipo-pagamento.type.ts b/src/entities/formasDePagamento/types/tipo-pagamento.type.ts index 3d65d74..6488a4c 100644 --- a/src/entities/formasDePagamento/types/tipo-pagamento.type.ts +++ b/src/entities/formasDePagamento/types/tipo-pagamento.type.ts @@ -3,7 +3,7 @@ * - `2`: Cheque * - `3`: Cartão de Crédito * - `4`: Cartão de Débito - * - `5`: Crédito Loja + * - `5`: Cartão da Loja (Private Label) * - `10`: Vale Alimentação * - `11`: Vale Refeição * - `12`: Vale Presente @@ -11,9 +11,12 @@ * - `14`: Duplicata Mercantil * - `15`: Boleto Bancário * - `16`: Depósito Bancário - * - `17`: Pagamento Instantâneo (PIX) + * - `17`: Pagamento Instantâneo (PIX) - Dinâmico * - `18`: Transferência Bancária, Carteira Digital * - `19`: Programa de Fidelidade, Cashback, Crédito Virtual + * - `20`: Pagamento Instantâneo (PIX) – Estático + * - `21`: Crédito em loja + * - `22`: Pagamento Eletrônico não Informado - falha de hardware do sistema emissor * - `90`: Sem pagamento * - `99`: Outros */ @@ -33,5 +36,8 @@ export type ITipoPagamento = | 17 | 18 | 19 + | 20 + | 21 + | 22 | 90 | 99 diff --git a/src/entities/gruposDeProdutos/__tests__/create-response.ts b/src/entities/gruposDeProdutos/__tests__/create-response.ts new file mode 100644 index 0000000..99a163f --- /dev/null +++ b/src/entities/gruposDeProdutos/__tests__/create-response.ts @@ -0,0 +1,12 @@ +export default { + data: { + id: 12345678 + } +} + +export const createRequestBody = { + nome: "Grupo 1", + grupoProdutoPai: { + id: 123456 + } +} diff --git a/src/entities/gruposDeProdutos/__tests__/delete-many-response.ts b/src/entities/gruposDeProdutos/__tests__/delete-many-response.ts new file mode 100644 index 0000000..8b76f21 --- /dev/null +++ b/src/entities/gruposDeProdutos/__tests__/delete-many-response.ts @@ -0,0 +1,30 @@ +export default { + data: { + alertas: [ + { + error: { + type: "VALIDATION_ERROR", + message: "Não foi possível salvar a venda", + description: "A venda não pode ser salva, pois ocorreram problemas em sua validação.", + fields: [ + { + code: 49, + msg: "Uma ou mais parcelas da venda possuem erros de validação", + element: "parcelas", + namespace: "VENDAS", + collection: [ + { + index: 1, + code: 12, + msg: "Id da forma de pagamento inválido.", + element: "formaPagamento", + namespace: "VENDAS" + } + ] + } + ] + } + } + ] + } +} diff --git a/src/entities/gruposDeProdutos/__tests__/delete-response.ts b/src/entities/gruposDeProdutos/__tests__/delete-response.ts new file mode 100644 index 0000000..7b85954 --- /dev/null +++ b/src/entities/gruposDeProdutos/__tests__/delete-response.ts @@ -0,0 +1 @@ +export default null diff --git a/src/entities/gruposDeProdutos/__tests__/find-response.ts b/src/entities/gruposDeProdutos/__tests__/find-response.ts new file mode 100644 index 0000000..b7d279c --- /dev/null +++ b/src/entities/gruposDeProdutos/__tests__/find-response.ts @@ -0,0 +1,10 @@ +export default { + data: { + id: 123456, + nome: "Grupo 1", + grupoProdutoPai: { + id: 123456, + nome: "Grupo 1" + } + } +} diff --git a/src/entities/gruposDeProdutos/__tests__/get-response.ts b/src/entities/gruposDeProdutos/__tests__/get-response.ts new file mode 100644 index 0000000..f0cead1 --- /dev/null +++ b/src/entities/gruposDeProdutos/__tests__/get-response.ts @@ -0,0 +1,12 @@ +export default { + data: [ + { + id: 123456, + nome: "Grupo 1", + grupoProdutoPai: { + id: 123456, + nome: "Grupo 1" + } + } + ] +} diff --git a/src/entities/gruposDeProdutos/__tests__/index.spec.ts b/src/entities/gruposDeProdutos/__tests__/index.spec.ts new file mode 100644 index 0000000..52fcfe8 --- /dev/null +++ b/src/entities/gruposDeProdutos/__tests__/index.spec.ts @@ -0,0 +1,142 @@ +import { Chance } from 'chance' +import { GruposDeProdutos } 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 { IGetResponse } from '../interfaces/get.interface' +import createResponse, { createRequestBody } from './create-response' +import deleteManyResponse from './delete-many-response' +import deleteResponse from './delete-response' +import findResponse from './find-response' +import getResponse from './get-response' +import updateResponse, { updateRequestBody } from './update-response' + +const chance = Chance() + +describe('GruposDeProdutos entity', () => { + let repository: InMemoryBlingRepository + let entity: GruposDeProdutos + + beforeEach(() => { + repository = new InMemoryBlingRepository() + entity = new GruposDeProdutos(repository) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('should delete many successfully', async () => { + const idsGruposProdutos: number[] = [] + for (let i = 0; i < chance.natural({ min: 1, max: 5 }); i++) { + idsGruposProdutos.push(chance.natural()) + } + const spy = jest.spyOn(repository, 'destroy') + repository.setResponse(deleteManyResponse) + + const response = await entity.deleteMany({ idsGruposProdutos }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'grupos-produtos', + id: '', + params: { idsGruposProdutos } + }) + expect(response).toBe(deleteManyResponse) + + const typingResponseTest: IDeleteManyResponse = deleteManyResponse + expect(typingResponseTest).toBe(deleteManyResponse) + }) + + it('should delete successfully', async () => { + const idGrupoProduto = chance.natural() + const spy = jest.spyOn(repository, 'destroy') + repository.setResponse(deleteResponse) + + const response = await entity.delete({ idGrupoProduto }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'grupos-produtos', + id: String(idGrupoProduto) + }) + expect(response).toBe(deleteResponse) + + const typingResponseTest: null = deleteResponse + expect(typingResponseTest).toBe(deleteResponse) + }) + + it('should get successfully', async () => { + const spy = jest.spyOn(repository, 'index') + repository.setResponse(getResponse) + + const response = await entity.get() + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'grupos-produtos', + params: { + nome: undefined, + nomePai: undefined, + pagina: undefined, + limite: undefined + } + }) + expect(response).toBe(getResponse) + + const typingResponseTest: IGetResponse = getResponse + expect(typingResponseTest).toBe(getResponse) + }) + + it('should find successfully', async () => { + const spy = jest.spyOn(repository, 'show') + const idGrupoProduto = chance.natural() + repository.setResponse(findResponse) + + const response = await entity.find({ idGrupoProduto }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'grupos-produtos', + id: String(idGrupoProduto) + }) + expect(response).toBe(findResponse) + + const typingResponseTest: IFindResponse = findResponse + expect(typingResponseTest).toBe(findResponse) + }) + + it('should create successfully', async () => { + const spy = jest.spyOn(repository, 'store') + repository.setResponse(createResponse) + + const response = await entity.create(createRequestBody) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'grupos-produtos', + 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 idGrupoProduto = chance.natural() + repository.setResponse(updateResponse) + + const response = await entity.update({ + idGrupoProduto, + ...updateRequestBody + }) + + expect(spy).toHaveBeenCalledWith({ + endpoint: 'grupos-produtos', + id: String(idGrupoProduto), + body: updateRequestBody + }) + expect(response).toBe(updateResponse) + + const typingResponseTest: null = updateResponse + expect(typingResponseTest).toBe(updateResponse) + }) +}) diff --git a/src/entities/gruposDeProdutos/__tests__/update-response.ts b/src/entities/gruposDeProdutos/__tests__/update-response.ts new file mode 100644 index 0000000..9481171 --- /dev/null +++ b/src/entities/gruposDeProdutos/__tests__/update-response.ts @@ -0,0 +1,8 @@ +export default null + +export const updateRequestBody = { + nome: "Grupo 1", + grupoProdutoPai: { + id: 123456 + } +} diff --git a/src/entities/gruposDeProdutos/index.ts b/src/entities/gruposDeProdutos/index.ts new file mode 100644 index 0000000..36f7563 --- /dev/null +++ b/src/entities/gruposDeProdutos/index.ts @@ -0,0 +1,135 @@ +import { Entity } from '../@shared/entity' +import { ICreateBody, ICreateResponse } from './interfaces/create.interface' +import { + IDeleteManyParams, + IDeleteManyResponse +} from './interfaces/delete-many.interface' +import { IDeleteParams } from './interfaces/delete.interface' +import { IFindParams, IFindResponse } from './interfaces/find.interface' +import { IGetParams, IGetResponse } from './interfaces/get.interface' +import { + IUpdateBody, + IUpdateParams +} from './interfaces/update.interface' + +/** + * Entidade para interação com Grupos de Produtos. + * + * @see https://developer.bling.com.br/referencia#/Grupos%20de%20Produtos + */ +export class GruposDeProdutos extends Entity { + /** + * Remove múltiplos grupos de produtos. + * + * @param {IDeleteManyParams} params Parâmetros da remoção. + * + * @returns {Promise} Não há retorno. + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Grupos%20de%20Produtos/delete_grupos_produtos + */ + public async deleteMany( + params: IDeleteManyParams + ): Promise { + return await this.repository.destroy({ + endpoint: 'grupos-produtos', + id: '', + params: { idsGruposProdutos: params.idsGruposProdutos } + }) + } + + /** + * Remove um grupo de produtos. + * + * @param {IDeleteParams} params Parâmetros da remoção. + * + * @returns {Promise} Não há retorno. + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Grupos%20de%20Produtos/delete_grupos_produtos__idGrupoProduto_ + */ + public async delete(params: IDeleteParams): Promise { + return await this.repository.destroy({ + endpoint: 'grupos-produtos', + id: String(params.idGrupoProduto) + }) + } + + /** + * Obtém grupos de produtos. + * + * @param {IGetParams} params Parâmetros da busca. + * + * @returns {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/GruposDeProdutos/get_produtos + */ + public async get(params?: IGetParams): Promise { + return await this.repository.index({ + endpoint: 'grupos-produtos', + params: { + nome: params?.nome, + nomePai: params?.nomePai, + pagina: params?.pagina, + limite: params?.limite, + } + }) + } + + /** + * Obtém um grupo de produtos. + * + * @param {IFindParams} params Parâmetros da busca. + * + * @returns {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Grupos%20de%20Produtos/get_grupos_produtos__idGrupoProduto_ + */ + public async find(params: IFindParams): Promise { + return await this.repository.show({ + endpoint: 'grupos-produtos', + id: String(params.idGrupoProduto) + }) + } + + /** + * Cria um grupo de produtos. + * + * @param {ICreateBody} body O conteúdo para a criação. + * + * @returns {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Grupos%20de%20Produtos/post_grupos_produtos + */ + public async create(body: ICreateBody): Promise { + return await this.repository.store({ + endpoint: 'grupos-produtos', + body + }) + } + + /** + * Altera um grupo de produtos. + * + * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização. + * + * @return {Promise} + * @throws {BlingApiException|BlingInternalException} + * + * @see https://developer.bling.com.br/referencia#/Grupos%20de%20Produtos/put_grupos_produtos__idGrupoProduto_ + */ + public async update( + params: IUpdateParams & IUpdateBody + ): Promise { + const { idGrupoProduto, ...body } = params + + return await this.repository.replace({ + endpoint: 'grupos-produtos', + id: String(idGrupoProduto), + body + }) + } +} diff --git a/src/entities/gruposDeProdutos/interfaces/create.interface.ts b/src/entities/gruposDeProdutos/interfaces/create.interface.ts new file mode 100644 index 0000000..1ad9216 --- /dev/null +++ b/src/entities/gruposDeProdutos/interfaces/create.interface.ts @@ -0,0 +1,12 @@ +export interface ICreateBody { + nome: string + grupoProdutoPai: { + id: number + } +} + +export interface ICreateResponse { + data: { + id: number + } +} diff --git a/src/entities/gruposDeProdutos/interfaces/delete-many.interface.ts b/src/entities/gruposDeProdutos/interfaces/delete-many.interface.ts new file mode 100644 index 0000000..8992c07 --- /dev/null +++ b/src/entities/gruposDeProdutos/interfaces/delete-many.interface.ts @@ -0,0 +1,14 @@ +import { IDefaultErrorResponse } from 'src/entities/@shared/interfaces/error.interface' + +export interface IDeleteManyParams { + /** + * IDs dos grupos de produtos + */ + idsGruposProdutos: number[] +} + +export interface IDeleteManyResponse { + data: { + alertas?: IDefaultErrorResponse[] + } +} diff --git a/src/entities/gruposDeProdutos/interfaces/delete.interface.ts b/src/entities/gruposDeProdutos/interfaces/delete.interface.ts new file mode 100644 index 0000000..fb1111e --- /dev/null +++ b/src/entities/gruposDeProdutos/interfaces/delete.interface.ts @@ -0,0 +1,6 @@ +export interface IDeleteParams { + /** + * ID do grupo de produto + */ + idGrupoProduto: number +} diff --git a/src/entities/gruposDeProdutos/interfaces/find.interface.ts b/src/entities/gruposDeProdutos/interfaces/find.interface.ts new file mode 100644 index 0000000..8ea448b --- /dev/null +++ b/src/entities/gruposDeProdutos/interfaces/find.interface.ts @@ -0,0 +1,24 @@ +export interface IFindParams { + /** + * ID do grupo de produto + */ + idGrupoProduto: number +} + +interface GruposProdutosDadosDTO { + id?: number + nome: string + grupoProdutoPai?: { + id: number + nome?: string + } +} + +interface GruposProdutosGrupoProdutoPaiDTO { + id: number + nome?: string +} + +export interface IFindResponse { + data: GruposProdutosDadosDTO | GruposProdutosGrupoProdutoPaiDTO +} diff --git a/src/entities/gruposDeProdutos/interfaces/get.interface.ts b/src/entities/gruposDeProdutos/interfaces/get.interface.ts new file mode 100644 index 0000000..e841c1e --- /dev/null +++ b/src/entities/gruposDeProdutos/interfaces/get.interface.ts @@ -0,0 +1,29 @@ +export interface IGetParams { + /** + * O nome do grupo + */ + nome?: string + /** + * O nome do grupo pai + */ + nomePai?: string + /** + * N° da página da listagem + */ + pagina?: number + /** + * Quantidade de registros que devem ser exibidos por página + */ + limite?: number +} + +export interface IGetResponse { + data: { + id?: number + nome: string + grupoProdutoPai?: { + id: number + nome?: string + } + }[] +} diff --git a/src/entities/gruposDeProdutos/interfaces/update.interface.ts b/src/entities/gruposDeProdutos/interfaces/update.interface.ts new file mode 100644 index 0000000..660a1ef --- /dev/null +++ b/src/entities/gruposDeProdutos/interfaces/update.interface.ts @@ -0,0 +1,13 @@ +export interface IUpdateParams { + /** + * ID do grupo de produto + */ + idGrupoProduto: number +} + +export interface IUpdateBody { + nome: string + grupoProdutoPai: { + id: number + } +}