diff --git a/.editorconfig b/.editorconfig
index b16ca2c..9142239 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,4 +10,4 @@ trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
-trim_trailing_whitespace = false
\ No newline at end of file
+trim_trailing_whitespace = false
diff --git a/.eslintrc.json b/.eslintrc.json
index 35fb922..b49c84d 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -6,11 +6,12 @@
"extends": [
"standard",
"plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended"
+ "plugin:@typescript-eslint/recommended",
+ "plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
- "ecmaVersion": 12,
+ "ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["@typescript-eslint"],
@@ -29,6 +30,12 @@
"rules": {
"camelcase": "off"
}
+ },
+ {
+ "files": ["*"],
+ "rules": {
+ "space-before-function-paren": "off"
+ }
}
]
}
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..373edcc
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+github: [AlexandreBellas]
+ko_fi: alebatistella
+custom: ["https://www.paypal.com/donate/?hosted_button_id=G2NJKZ5MUMKBS"]
diff --git a/.github/workflows/on-commit-test.yaml b/.github/workflows/on-commit-test.yaml
new file mode 100644
index 0000000..4618f94
--- /dev/null
+++ b/.github/workflows/on-commit-test.yaml
@@ -0,0 +1,21 @@
+name: On commit => execute test workflow
+
+on: push
+
+jobs:
+ unit-feature-tests:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-node@v3
+ with:
+ node-version: 20
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Build project
+ run: npm run build
+
+ - name: Execute tests
+ run: npm run test
diff --git a/.prettierrc.json b/.prettierrc.json
index 68c8428..855a65a 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,6 +1,6 @@
{
- "trailingComma": "none",
- "tabWidth": 2,
- "semi": false,
- "singleQuote": true
-}
+ "trailingComma": "none",
+ "tabWidth": 2,
+ "semi": false,
+ "singleQuote": true
+}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index a2ca4f3..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-language: node_js
-node_js:
- - 14.18.1
-cache: npm
-install:
- - npm install
- - npm ci
-script:
- - npm run format
- - npm run lint
- - npm run build
- - npm run test:coveralls
-deploy:
- provider: npm
- skip_cleanup: true
- email: alexandre.bellas@gmail.com
- tag: next
- api_key: $NPM_TOKEN
- on:
- branch: main
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..aefbb7d
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,32 @@
+{
+ "material-icon-theme.folders.associations": {
+ "@shared": "Shared",
+ "exceptions": "Error"
+ },
+ "cSpell.words": [
+ "aliquota",
+ "Amazônia",
+ "borderô",
+ "Borderos",
+ "borderôs",
+ "Contabeis",
+ "Contábeis",
+ "Contabil",
+ "contábil",
+ "CONTRAN",
+ "customizado",
+ "Customizados",
+ "Eletrônicas",
+ "Frotista",
+ "ICMS",
+ "ICMSST",
+ "ICRT",
+ "intermediador",
+ "nfces",
+ "Parcelada",
+ "pickone",
+ "Rastreamento",
+ "sublimite",
+ "SUFRAMA"
+ ]
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..934ffbd
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,45 @@
+# Contribuição ao projeto
+
+Agradeço muito o interesse de apoio ao projeto. Para uma contribuição saudável e
+inclusiva, devemos manter um padrão de execução.
+
+## Como começar a contribuição
+
+1. Crie uma _issue_ explicando o problema e o desejo de alteração;
+2. Após receber um retorno validando o problema e permitindo a alteração, faça
+ um novo `fork` do repositório;
+3. Implemente as mudanças ou novas funcionalidades que deseja realizar em seu
+ `fork`;
+4. Crie um novo _pull request_ para a _branch_ `develop` do repositório oficial.
+
+## Padrões de projeto
+
+- Todas as variáveis e nomes de funções devem estar em `camelCase`;
+- Todas as classes devem estar em `PascalCase`;
+- Todas as entidades devem seguir o padrão:
+ 1. Uma pasta para cada entidade;
+ 2. Um arquivo para cada rota disponibilizada em relação aos envios/retornos;
+ 3. Um arquivo principal de declaração e utilização da entidade;
+ 4. Teste unitário de chamada e tipagem de retorno.
+- Deve-se manter o padrão do código baseado nas configurações do **prettier** e
+ do **eslint** do projeto.
+
+### Mensagens de _commits_
+
+Siga o padrão [_conventional commits_](https://www.conventionalcommits.org/en/v1.0.0/).
+
+## Adicionar uma nova linguagem de programação ao projeto
+
+1. Crie uma _issue_ anunciando o desejo de trabalhar em uma nova linguagem de
+ programação;
+2. A sua _issue_ será fixada para que outras pessoas possam acompanhar e apoiar
+ a sua implementação;
+3. Realize a sua implementação em uma nova pasta na raiz do projeto com o nome
+ da linguagem de programação em letras minúsculas (por exemplo: Java seria
+ criado como `java`, Elixir seria criado como `elixir`, etc);
+4. Crie um _pull request_ para a _branch_ `develop` do repositório oficial a
+ cada nova entidade implementada.
+
+A ideia é ter o pacote já inicializado e disponibilizado mesmo que nem todas as
+entidades estejam disponíveis. Isso permite uma contribuição mais facilitada de
+outros desenvolvedores.
diff --git a/README.md b/README.md
index ebe27f8..fdb40f8 100644
--- a/README.md
+++ b/README.md
@@ -4,10 +4,19 @@
[![install size](https://packagephobia.com/badge?p=bling-erp-api)](https://packagephobia.com/result?p=bling-erp-api)
[![code coverage](https://coveralls.io/repos/github/AlexandreBellas/bling-erp-api/badge.svg?branch=main)](https://coveralls.io/github/AlexandreBellas/bling-erp-api?branch=main)
-> ### A biblioteca atualmente usa a API v2 do Bling. Uma nova versão para uso da API v3 está em desenvolvimento no momento, com previsão de ser disponibilizada entre Dezembro/2023 e Janeiro/2024.
+Pacote de integração com a [API v3 do ERP Bling](https://developer.bling.com.br).
+O mais completo existente (e se não é, será).
-Pacote de integração com a [API do ERP Bling](https://ajuda.bling.com.br/hc/pt-br/categories/360002186394-API-para-Desenvolvedores). O mais completo existente (e se não é, será).
-Disponível também para **Typescript**.
+Disponível para:
+
+- [x] JavaScript
+- [x] TypeScript
+- [ ] PHP (em breve)
+- [ ] C# (em breve)
+
+**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).
## Instalação
@@ -22,102 +31,99 @@ npm i bling-erp-api
### CommonJS
```js
-const { Bling } = require('bling-erp-api')
+const Bling = require('bling-erp-api')
```
### ES6
```ts
-import { Bling } from 'bling-erp-api'
+import Bling from 'bling-erp-api'
```
## Criação de uma nova conexão
-Para criar uma conexão ao serviço do Bling, basta instanciar o objeto com a [API
-key](https://ajuda.bling.com.br/hc/pt-br/articles/360046937853-Introdu%C3%A7%C3%A3o-para-a-API-do-Bling-para-desenvolvedores-) em
-seu construtor.
+Para criar uma conexão ao serviço do Bling, basta instanciar o objeto com a [API key](https://developer.bling.com.br/autenticacao) em seu construtor.
```js
const apiKey = 'sua_api_key'
const blingConnection = new Bling(apiKey)
```
+Vale destacar que o fluxo de criação e autorização do aplicativo **não é feito
+pela biblioteca**. Ou seja, a biblioteca somente recebe o `access_token` gerado
+a partir do _endpoint_ `/token`. [Veja a referência](https://developer.bling.com.br/aplicativos#tokens-de-acesso).
+
+Para entender na prática como a autenticação citada acima funciona, [veja o
+projeto de demonstração](https://github.com/AlexandreBellas/bling-erp-api/tree/main/demo).
+
## Entidades disponíveis
-As entidades atualmente permitidas para interação são:
-
-- Borderos (`.borderos()`)
-- Campos customizados (`.customizedFields()` ou `.camposCustomizados()`)
-- Categorias (`.categories()` ou `.categorias()`)
-- Categorias Loja (`.shopCategories()` ou `.categoriasLoja()`)
-- Contatos (`.contacts()` ou `.contatos()`)
-- Contas a pagar (`.billsToPay()` ou `.contasAPagar()`)
-- Contas a receber (`.billsToReceive()` ou `.contasAReceber()`)
-- Contratos (`.contracts()` ou `.contratos()`)
-- CTes (`.ctes()`)
-- Depósitos (`.deposits()` ou `.depositos()`)
-- Formas de pagamento (`.paymentMethods()` ou `.formasDePagamento()`)
-- Grupo de produtos (`.productGroups()` ou `.grupoDeProdutos()`)
-- NFCes (`.nfces()`)
-- Notas fiscais (`.invoices()` ou `.notasFiscais()`)
-- Notas de serviço (`.serviceInvoices()` ou `.notasServicos()`)
-- Pedidos (`.orders()` ou `.pedidos()`)
-- Pedidos de compra (`.purchaseOrders()` ou `.pedidosDeCompra()`)
-- Produtos (`.products()` ou `.produtos()`)
-- Propostas comerciais (`.commercialProposals()` ou `.propostasComerciais()`)
-
-Ainda estão em desenvolvimento as entidades:
-
-- Logística
-- Ordem de produção
-- Produto Fornecedores
-- Produto Loja
-
-Adicionaremos as restantes de acordo com as _releases_.
-
-## Métodos permitidos
-
-- `all()`: retorna todos os registros da entidade
-- `find()`: retorna um registro da entidade desejada através de seu `id` ou
- `codigo`
-- `findBy()`: retorna os registros da entidade **que se adequem aos filtros
- passados**
-- `create()`: cria um registro da entidade
-- `update()`: atualiza um registro da entidade a partir de seu `id` ou
- `codigo`
-- `delete()`: remove um registro da entidade a partir de seu `id` ou
- `codigo`
-
-Nem todas as entidades possuem interação com todos os métodos (de acordo com a
-documentação da API do Bling). Ao utilizar o pacote e estar no VSCode, se o
-desenvolvedor utilizar intelliSense ao programar, os métodos permitidos
-aparecerão automaticamente após usar o atalho `Ctrl` + `Barra de espaço`.
+Todas as entidades do Bling atualmente são permitidas para interação. São elas:
+
+- [x] Borderos (`.borderos`)
+- [x] Campos customizados (`.camposCustomizados`)
+- [x] Categorias - Lojas (`.categoriasLojas`)
+- [x] Categorias - Produtos (`.categoriasProdutos`)
+- [x] Categorias - Receitas e Despesas (`.categoriasReceitasDespesas`)
+- [x] Contas a Pagar (`.contasPagar`)
+- [x] Contas a Receber (`.contasReceber`)
+- [x] Contas Contábeis (`.contasContabeis`)
+- [x] Contatos (`.contatos`)
+- [x] Contatos - Tipos (`.contatosTipos`)
+- [x] Contratos (`.contratos`)
+- [x] Depósitos (`.depositos`)
+- [x] Empresas (`.empresas`)
+- [x] Estoques (`.estoques`)
+- [x] Formas de pagamento (`.formasDePagamento`)
+- [x] Homologação (`.homologacao`)
+- [x] Logísticas (`.logisticas`)
+- [x] Logísticas - Etiquetas (`.logisticasEtiquetas`)
+- [x] Logísticas - Objetos (`.logisticasObjetos`)
+- [x] Logísticas - Serviços (`.logisticasServicos`)
+- [x] Naturezas de Operações (`.naturezasDeOperacoes`)
+- [x] Notas Fiscais de Consumidor Eletrônicas (`.nfces`)
+- [x] Notas Fiscais de Serviço Eletrônicas (`.nfses`)
+- [x] Notas Fiscais Eletrônicas (`.nfes`)
+- [x] Notificações (`.notificacoes`)
+- [x] Pedidos - Compras (`.pedidosCompras`)
+- [x] Pedidos - Vendas (`.pedidosVendas`)
+- [x] Produtos (`.produtos`)
+- [x] Produtos - Estruturas (`.produtosEstruturas`)
+- [x] Produtos - Fornecedores (`.produtosFornecedores`)
+- [x] Produtos - Lojas (`.produtosLojas`)
+- [x] Produtos - Variações (`.produtosVariacoes`)
+- [x] Situações (`.situacoes`)
+- [x] Situações - Módulos (`.situacoesModulos`)
+- [x] Situações - Transições (`.situacoesTransicoes`)
+- [x] Usuários (`.usuarios`)
+- [x] Vendedores (`.vendedores`)
## Exemplo de uso
-Para listar todos os produtos, basta executar:
+Para listar seus produtos, basta executar:
```js
-// Também disponível pelo método:
-// import { Bling } from 'bling-erp-api'
-const { Bling } = require('bling-erp-api')
+// Também disponível como:
+// import Bling from 'bling-erp-api'
+const Bling = require('bling-erp-api')
const apiKey = 'sua_api_key'
const blingConnection = new Bling(apiKey)
-const products = await blingConnection.products().all()
+const products = await blingConnection.produtos.get()
console.log(products)
```
-## Executando testes automatizados
-Para isso, faça o clone do projeto e execute
+## Executando os testes do projeto
+
+Faça o clone do projeto, instale as dependências e execute:
```bash
npm run test
```
-## Contribuição
+## Recursos
-Basta fazer um _fork_ do projeto e abrir novos _Pull Requests_ ou interagir
-conosco abrindo _issues_ sobre os problemas encontrados.
+- [Guia de contribuição](https://github.com/AlexandreBellas/bling-erp-api/blob/v5.0.0/CONTRIBUTING.md)
+- [Apoie o projeto](https://www.paypal.com/donate/?hosted_button_id=G2NJKZ5MUMKBS)
diff --git a/commitlint.config.js b/commitlint.config.js
deleted file mode 100644
index 4fedde6..0000000
--- a/commitlint.config.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = { extends: ['@commitlint/config-conventional'] }
diff --git a/demo/README.md b/demo/README.md
new file mode 100644
index 0000000..1060f29
--- /dev/null
+++ b/demo/README.md
@@ -0,0 +1,27 @@
+# Projeto de demonstração
+
+## Aplicativo no Bling
+
+Crie um aplicativo para teste [como esta página descreve](https://developer.bling.com.br/aplicativos#introdu%C3%A7%C3%A3o).
+
+É obrigatório que você crie com a URL de redirecionamento como `http://localhost:3000/auth`.
+
+Além disso, para fins de teste, conceda a permissão para listar produtos.
+
+## Execução
+
+Inicie o servidor executando:
+
+```bash
+npm i
+npm run dev
+```
+
+Abra o arquivo `index.html` e siga o que é apresentado.
+
+### Passo a passo
+
+1. Insira o _client ID_ e o _client secret_;
+2. Conceda as permissões pedidas;
+3. O _token_ de acesso aparecerá em seu navegador em formato JSON. Além disso,
+ será feita uma listagem de seus produtos ilustrando o uso da biblioteca.
diff --git a/demo/index.html b/demo/index.html
new file mode 100644
index 0000000..9572445
--- /dev/null
+++ b/demo/index.html
@@ -0,0 +1,19 @@
+
+
+
+ Demo Bling ERP API
+
+
+
+
+
diff --git a/demo/package.json b/demo/package.json
new file mode 100644
index 0000000..12e01dd
--- /dev/null
+++ b/demo/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "demo",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "tsc",
+ "dev": "ts-node-dev --inspect --transpile-only --ignore-watch node_modules server.ts"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "express": "^4.18.2"
+ },
+ "devDependencies": {
+ "@types/express": "^4.17.21",
+ "typescript": "^5.3.3"
+ }
+}
\ No newline at end of file
diff --git a/demo/server.ts b/demo/server.ts
new file mode 100644
index 0000000..8f51f87
--- /dev/null
+++ b/demo/server.ts
@@ -0,0 +1,53 @@
+import Bling from 'bling-erp-api'
+import express, { Request, Response } from 'express'
+import fs from 'fs'
+
+const tmpFile = './access.json'
+const app = express()
+app.use(express.urlencoded({ extended: true }))
+
+app.post('/', (req: Request, res: Response) => {
+ const { client_id: clientId, client_secret: clientSecret } = req.body
+ const baseUrl = 'https://www.bling.com.br'
+ const endpoint = 'Api/v3/oauth/authorize'
+ const state = '1234567890'
+ const redirectUri = 'http://localhost:3000/auth'
+
+ fs.writeFileSync(tmpFile, JSON.stringify({ clientId, clientSecret }))
+
+ res.redirect(
+ `${baseUrl}/${endpoint}?response_type=code&client_id=${clientId}&state=${state}&redirect_uri=${redirectUri}`
+ )
+})
+
+app.get('/auth', (req: Request, res: Response) => {
+ const { code } = req.query
+
+ const { clientId, clientSecret } = JSON.parse(
+ fs.readFileSync(tmpFile).toString()
+ )
+ fs.rmSync(tmpFile)
+
+ const authKey = `${clientId}:${clientSecret}`
+
+ fetch('https://www.bling.com.br/Api/v3/oauth/token', {
+ method: 'POST',
+ body: `grant_type=authorization_code&code=${code}`,
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ Authorization: `Basic ${btoa(authKey)}`
+ }
+ }).then((response) => {
+ response.json().then((content) => {
+ const bling = new Bling(content.access_token)
+ bling.produtos.get().then((products) => {
+ res.json({
+ ...content,
+ products
+ })
+ })
+ })
+ })
+})
+
+app.listen(3000, () => 'server running on port 3000')
diff --git a/demo/tsconfig.json b/demo/tsconfig.json
new file mode 100644
index 0000000..f733fce
--- /dev/null
+++ b/demo/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "es2016",
+ "module": "commonjs",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "skipLibCheck": true,
+ "outDir": "./lib",
+ "rootDir": "."
+ }
+}
diff --git a/jest.config.json b/jest.config.json
deleted file mode 100644
index b2e6204..0000000
--- a/jest.config.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "transform": {
- "^.+\\.(t|j)sx?$": "ts-jest"
- },
- "testRegex": "(/test/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
- "moduleFileExtensions": [
- "ts",
- "tsx",
- "js",
- "jsx",
- "json",
- "node"
- ],
- "transformIgnorePatterns": [
- "/node_modules/",
- "\\.pnp\\.[^\\\/]+$"
- ],
- "testPathIgnorePatterns": [
- "/test/config/",
- "/test/generators/"
- ],
- "maxWorkers": 1,
- "maxConcurrency": 2
-}
diff --git a/jest.config.ts b/jest.config.ts
new file mode 100644
index 0000000..303b5c3
--- /dev/null
+++ b/jest.config.ts
@@ -0,0 +1,22 @@
+import type { Config } from 'jest'
+
+const config: Config = {
+ transform: {
+ '^.+\\.(t|j)sx?$': 'ts-jest'
+ },
+ testRegex: '(/test/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
+ transformIgnorePatterns: [
+ '/node_modules/',
+ '\\.pnp\\.[^\\/]+$',
+ '/coverage/'
+ ],
+ testPathIgnorePatterns: ['/lib/'],
+ maxWorkers: 1,
+ maxConcurrency: 2,
+ collectCoverageFrom: ['**/*.(t|j)s'],
+ coveragePathIgnorePatterns: ['/coverage/', 'jest.config.ts', '/lib/'],
+ coverageDirectory: './coverage'
+}
+
+export default config
diff --git a/package.json b/package.json
index be1fa4b..2c0840f 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "bling-erp-api",
- "version": "4.0.0",
- "description": "Pacote de interação com a REST API do serviço Bling ERP",
+ "version": "5.0.0",
+ "description": "Pacote de integração com a API do Bling ERP",
"main": "lib/bling.js",
"directories": {
"test": "test"
@@ -10,25 +10,30 @@
"lib/**/*"
],
"scripts": {
- "build": "tsc",
+ "build": "tsc --project tsconfig.build.json",
"format": "prettier --write 'src/**/*.ts'",
"lint": "eslint --fix 'src/**/*.ts'",
- "test": "npm run build && jest --config jest.config.json",
- "test:coveralls": "jest --config jest.config.json --coverage && coveralls < coverage/lcov.info",
- "prepare": "npm run build",
- "prepublishOnly": "npm test && npm run lint",
- "preversion": "npm run format",
- "version": "npm run lint && git add -A src",
- "postversion": "git push && git push --tags"
+ "test": "npm run build && jest --config jest.config.ts",
+ "test:coverage": "jest --config jest.config.ts --coverage"
},
"repository": {
"type": "git",
"url": "git+https://github.com/AlexandreBellas/bling-erp-api.git"
},
"keywords": [
- "bling",
+ "javascript",
+ "api",
+ "php",
+ "typescript",
+ "csharp",
+ "integration",
+ "js",
"erp",
- "api"
+ "ts",
+ "nfe",
+ "sefaz",
+ "bling",
+ "bling-erp"
],
"author": "AlexandreBellas; vitor-san",
"license": "ISC",
@@ -36,63 +41,32 @@
"url": "https://github.com/AlexandreBellas/bling-erp-api/issues"
},
"homepage": "https://github.com/AlexandreBellas/bling-erp-api#readme",
- "husky": {
- "hooks": {
- "pre-commit": "lint-staged"
- }
- },
- "lint-staged": {
- "*.js": [
- "prettier --write",
- "eslint --fix",
- "git add"
- ],
- "*.ts": [
- "prettier --write",
- "eslint --fix",
- "git add"
- ]
- },
"dependencies": {
- "axios": "^0.23.0",
- "xml2js": "^0.4.23"
+ "axios": "^1.6.2"
},
"devDependencies": {
- "@commitlint/cli": "^11.0.0",
- "@commitlint/config-conventional": "^11.0.0",
"@types/chance": "^1.1.3",
"@types/jest": "^27.5.2",
"@types/uuid": "^8.3.3",
- "@types/xml2js": "^0.4.9",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"chance": "^1.1.8",
- "commitizen": "^4.2.4",
- "coveralls": "^3.1.1",
"cpf_cnpj": "^0.2.0",
- "cross-env": "^7.0.3",
"dotenv": "^10.0.0",
- "eslint": "^7.32.0",
- "eslint-config-standard": "^16.0.3",
- "eslint-plugin-import": "^2.25.2",
+ "eslint": "^8.0.0",
+ "eslint-config-prettier": "^9.0.0",
+ "eslint-config-standard": "^17.1.0",
+ "eslint-plugin-import": "^2.29.0",
"eslint-plugin-node": "^11.1.0",
- "eslint-plugin-promise": "^5.1.0",
- "husky": "^4.3.6",
- "jest": "^27.3.0",
- "lint-staged": "^10.5.3",
+ "eslint-plugin-promise": "^6.1.1",
+ "jest": "^29.7.0",
"prettier": "^2.4.1",
- "travis": "^0.1.1",
- "ts-jest": "^27.0.7",
+ "ts-jest": "^29.1.1",
"ts-node": "^10.3.0",
- "typescript": "^4.4.4",
+ "typescript": "^5.0.0",
"uuid": "^8.3.2"
},
- "config": {
- "commitizen": {
- "path": "./node_modules/cz-conventional-changelog"
- }
- },
"publishConfig": {
"access": "public"
}
-}
+}
\ No newline at end of file
diff --git a/src/bling.spec.ts b/src/bling.spec.ts
new file mode 100644
index 0000000..856e380
--- /dev/null
+++ b/src/bling.spec.ts
@@ -0,0 +1,241 @@
+'use strict'
+
+import { Chance } from 'chance'
+import Bling from './bling'
+import { Borderos } from './entities/borderos'
+import { CamposCustomizados } from './entities/camposCustomizados'
+import { CategoriasLojas } from './entities/categoriasLojas'
+import { CategoriasProdutos } from './entities/categoriasProdutos'
+import { CategoriasReceitasDespesas } from './entities/categoriasReceitasDespesas'
+import { ContasContabeis } from './entities/contasContabeis'
+import { ContasPagar } from './entities/contasPagar'
+import { ContasReceber } from './entities/contasReceber'
+import { Contatos } from './entities/contatos'
+import { ContatosTipos } from './entities/contatosTipos'
+import { Contratos } from './entities/contratos'
+import { Depositos } from './entities/depositos'
+import { Empresas } from './entities/empresas'
+import { Estoques } from './entities/estoques'
+import { FormasDePagamento } from './entities/formasDePagamento'
+import { Homologacao } from './entities/homologacao'
+import { Logisticas } from './entities/logisticas'
+import { LogisticasEtiquetas } from './entities/logisticasEtiquetas'
+import { LogisticasObjetos } from './entities/logisticasObjetos'
+import { LogisticasServicos } from './entities/logisticasServicos'
+import { NaturezasDeOperacoes } from './entities/naturezasDeOperacoes'
+import { Nfces } from './entities/nfces'
+import { Nfes } from './entities/nfes'
+import { Nfses } from './entities/nfses'
+import { Notificacoes } from './entities/notificacoes'
+import { PedidosCompras } from './entities/pedidosCompras'
+import { PedidosVendas } from './entities/pedidosVendas'
+import { Produtos } from './entities/produtos'
+import { ProdutosEstruturas } from './entities/produtosEstruturas'
+import { ProdutosFornecedores } from './entities/produtosFornecedores'
+import { ProdutosLojas } from './entities/produtosLojas'
+import { ProdutosVariacoes } from './entities/produtosVariacoes'
+import { Situacoes } from './entities/situacoes'
+import { SituacoesModulos } from './entities/situacoesModulos'
+import { SituacoesTransicoes } from './entities/situacoesTransicoes'
+import { Usuarios } from './entities/usuarios'
+import { Vendedores } from './entities/vendedores'
+
+const chance = Chance()
+
+const createBling = (accessToken: string) => {
+ return new Bling(accessToken)
+}
+
+describe('Bling main module', () => {
+ it('should instantiate correctly', () => {
+ expect(createBling(chance.word())).toBeInstanceOf(Bling)
+ })
+
+ it('should retrieve borderôs entity', () => {
+ expect(createBling(chance.word()).borderos).toBeInstanceOf(Borderos)
+ })
+
+ it('should retrieve campos customizados entity', () => {
+ expect(createBling(chance.word()).camposCustomizados).toBeInstanceOf(
+ CamposCustomizados
+ )
+ })
+
+ it('should retrieve categorias - lojas entity', () => {
+ expect(createBling(chance.word()).categoriasLojas).toBeInstanceOf(
+ CategoriasLojas
+ )
+ })
+
+ it('should retrieve categorias - produtos entity', () => {
+ expect(createBling(chance.word()).categoriasProdutos).toBeInstanceOf(
+ CategoriasProdutos
+ )
+ })
+
+ it('should retrieve categorias - receitas e despesas entity', () => {
+ expect(
+ createBling(chance.word()).categoriasReceitasDespesas
+ ).toBeInstanceOf(CategoriasReceitasDespesas)
+ })
+
+ it('should retrieve contas a pagar entity', () => {
+ expect(createBling(chance.word()).contasPagar).toBeInstanceOf(ContasPagar)
+ })
+
+ it('should retrieve contas a receber entity', () => {
+ expect(createBling(chance.word()).contasReceber).toBeInstanceOf(
+ ContasReceber
+ )
+ })
+
+ it('should retrieve contas contábeis entity', () => {
+ expect(createBling(chance.word()).contasContabeis).toBeInstanceOf(
+ ContasContabeis
+ )
+ })
+
+ it('should retrieve contatos entity', () => {
+ expect(createBling(chance.word()).contatos).toBeInstanceOf(Contatos)
+ })
+
+ it('should retrieve contatos - tipos entity', () => {
+ expect(createBling(chance.word()).contatosTipos).toBeInstanceOf(
+ ContatosTipos
+ )
+ })
+
+ it('should retrieve contratos entity', () => {
+ expect(createBling(chance.word()).contratos).toBeInstanceOf(Contratos)
+ })
+
+ it('should retrieve depósitos entity', () => {
+ expect(createBling(chance.word()).depositos).toBeInstanceOf(Depositos)
+ })
+
+ it('should retrieve empresas entity', () => {
+ expect(createBling(chance.word()).empresas).toBeInstanceOf(Empresas)
+ })
+
+ it('should retrieve estoques entity', () => {
+ expect(createBling(chance.word()).estoques).toBeInstanceOf(Estoques)
+ })
+
+ it('should retrieve formas de pagamento entity', () => {
+ expect(createBling(chance.word()).formasDePagamento).toBeInstanceOf(
+ FormasDePagamento
+ )
+ })
+
+ it('should retrieve homologação entity', () => {
+ expect(createBling(chance.word()).homologacao).toBeInstanceOf(Homologacao)
+ })
+
+ it('should retrieve logísticas entity', () => {
+ expect(createBling(chance.word()).logisticas).toBeInstanceOf(Logisticas)
+ })
+
+ it('should retrieve logísticas - etiquetas entity', () => {
+ expect(createBling(chance.word()).logisticasEtiquetas).toBeInstanceOf(
+ LogisticasEtiquetas
+ )
+ })
+
+ it('should retrieve logísticas - objetos entity', () => {
+ expect(createBling(chance.word()).logisticasObjetos).toBeInstanceOf(
+ LogisticasObjetos
+ )
+ })
+
+ it('should retrieve logísticas - serviços entity', () => {
+ expect(createBling(chance.word()).logisticasServicos).toBeInstanceOf(
+ LogisticasServicos
+ )
+ })
+
+ it('should retrieve naturezas de operações entity', () => {
+ expect(createBling(chance.word()).naturezasDeOperacoes).toBeInstanceOf(
+ NaturezasDeOperacoes
+ )
+ })
+
+ it('should retrieve notas fiscais de consumidor eletrônicas entity', () => {
+ expect(createBling(chance.word()).nfces).toBeInstanceOf(Nfces)
+ })
+
+ it('should retrieve notas fiscais de serviço eletrônicas entity', () => {
+ expect(createBling(chance.word()).nfses).toBeInstanceOf(Nfses)
+ })
+
+ it('should retrieve notas fiscais eletrônicas entity', () => {
+ expect(createBling(chance.word()).nfes).toBeInstanceOf(Nfes)
+ })
+
+ it('should retrieve notificações entity', () => {
+ expect(createBling(chance.word()).notificacoes).toBeInstanceOf(Notificacoes)
+ })
+
+ it('should retrieve pedidos - compras entity', () => {
+ expect(createBling(chance.word()).pedidosCompras).toBeInstanceOf(
+ PedidosCompras
+ )
+ })
+
+ it('should retrieve pedidos - vendas entity', () => {
+ expect(createBling(chance.word()).pedidosVendas).toBeInstanceOf(
+ PedidosVendas
+ )
+ })
+
+ it('should retrieve produtos entity', () => {
+ expect(createBling(chance.word()).produtos).toBeInstanceOf(Produtos)
+ })
+
+ it('should retrieve produtos - estruturas entity', () => {
+ expect(createBling(chance.word()).produtosEstruturas).toBeInstanceOf(
+ ProdutosEstruturas
+ )
+ })
+
+ it('should retrieve produtos - fornecedores entity', () => {
+ expect(createBling(chance.word()).produtosFornecedores).toBeInstanceOf(
+ ProdutosFornecedores
+ )
+ })
+
+ it('should retrieve produtos - lojas entity', () => {
+ expect(createBling(chance.word()).produtosLojas).toBeInstanceOf(
+ ProdutosLojas
+ )
+ })
+
+ it('should retrieve produtos - variações entity', () => {
+ expect(createBling(chance.word()).produtosVariacoes).toBeInstanceOf(
+ ProdutosVariacoes
+ )
+ })
+
+ it('should retrieve situações entity', () => {
+ expect(createBling(chance.word()).situacoes).toBeInstanceOf(Situacoes)
+ })
+
+ it('should retrieve situações - módulos entity', () => {
+ expect(createBling(chance.word()).situacoesModulos).toBeInstanceOf(
+ SituacoesModulos
+ )
+ })
+
+ it('should retrieve situações - transições entity', () => {
+ expect(createBling(chance.word()).situacoesTransicoes).toBeInstanceOf(
+ SituacoesTransicoes
+ )
+ })
+
+ it('should retrieve usuários entity', () => {
+ expect(createBling(chance.word()).usuarios).toBeInstanceOf(Usuarios)
+ })
+
+ it('should retrieve vendedores entity', () => {
+ expect(createBling(chance.word()).vendedores).toBeInstanceOf(Vendedores)
+ })
+})
diff --git a/src/bling.ts b/src/bling.ts
index ca641e5..6d85184 100644
--- a/src/bling.ts
+++ b/src/bling.ts
@@ -1,332 +1,423 @@
'use strict'
-import Borderos from './entities/borderos'
-import CustomizedField from './entities/customizedFields'
-import Categories from './entities/categories'
-import CommercialProposals from './entities/commercialProposals'
-import Contacts from './entities/contacts'
-import Deposits from './entities/deposits'
-import Products from './entities/products'
-import Orders from './entities/orders'
-import PurchaseOrders from './entities/purchaseOrders'
-import Invoices from './entities/invoices'
-import ShopCategories from './entities/shopCategories'
-import BillsToPay from './entities/billsToPay'
-import BillsToReceive from './entities/billsToReceive'
-import Contracts from './entities/contracts'
-import Ctes from './entities/ctes'
-import PaymentMethods from './entities/paymentMethods'
-import ProductGroups from './entities/productGroups'
-import Nfces from './entities/nfces'
-import ServiceInvoices from './entities/serviceInvoices'
-
-import createError, {
- IBlingError as IStandardBlingError
-} from './core/helpers/createError'
-
-import { IApiInstance } from './core/interfaces/method'
-
-import * as qs from 'querystring'
-import axios from 'axios'
-
-export type IBorderos = ReturnType
-export type ICustomizedFields = ReturnType
-export type ICategories = ReturnType
-export type ICommercialProposals = ReturnType
-export type IContacts = ReturnType
-export type IDeposits = ReturnType
-export type IProducts = ReturnType
-export type IOrders = ReturnType
-export type IPurchaseOrders = ReturnType
-export type IInvoices = ReturnType
-export type IShopCategories = ReturnType
-export type IBillsToPay = ReturnType
-export type IBillsToReceive = ReturnType
-export type IContracts = ReturnType
-export type ICtes = ReturnType
-export type IPaymentMethods = ReturnType
-export type IProductGroups = ReturnType
-export type INfces = ReturnType
-export type IServiceInvoices = ReturnType
-
-export type IBlingError = IStandardBlingError
-
-export class Bling {
- #api: IApiInstance
- #raw: boolean
-
- #borderos: IBorderos | undefined
- #customizedFields: ICustomizedFields | undefined
- #categories: ICategories | undefined
- #commercialProposals: ICommercialProposals | undefined
- #contacts: IContacts | undefined
- #deposits: IDeposits | undefined
- #orders: IOrders | undefined
- #products: IProducts | undefined
- #purchaseOrders: IPurchaseOrders | undefined
- #invoices: IInvoices | undefined
- #shopCategories: IShopCategories | undefined
- #billsToPay: IBillsToPay | undefined
- #billsToReceive: IBillsToReceive | undefined
- #contracts: IContracts | undefined
- #ctes: ICtes | undefined
- #paymentMethods: IPaymentMethods | undefined
- #productGroups: IProductGroups | undefined
- #nfces: INfces | undefined
- #serviceInvoices: IServiceInvoices | undefined
-
- constructor (apiKey: string, options: { raw: boolean } = { raw: false }) {
- if (!apiKey || typeof apiKey !== 'string') {
- throw createError({
- name: 'BlingModuleError',
- message: "The API key wasn't correctly provided for Bling connection.",
- status: '500',
- data: {
- apiKey: apiKey
- },
- code: 'ERR_NO_API_KEY'
- })
+import { Entity } from './entities/@shared/entity'
+import { Borderos } from './entities/borderos'
+import { CamposCustomizados } from './entities/camposCustomizados'
+import { CategoriasLojas } from './entities/categoriasLojas'
+import { CategoriasProdutos } from './entities/categoriasProdutos'
+import { CategoriasReceitasDespesas } from './entities/categoriasReceitasDespesas'
+import { ContasContabeis } from './entities/contasContabeis'
+import { ContasPagar } from './entities/contasPagar'
+import { ContasReceber } from './entities/contasReceber'
+import { Contatos } from './entities/contatos'
+import { ContatosTipos } from './entities/contatosTipos'
+import { Contratos } from './entities/contratos'
+import { Depositos } from './entities/depositos'
+import { Empresas } from './entities/empresas'
+import { Estoques } from './entities/estoques'
+import { FormasDePagamento } from './entities/formasDePagamento'
+import { Homologacao } from './entities/homologacao'
+import { Logisticas } from './entities/logisticas'
+import { LogisticasEtiquetas } from './entities/logisticasEtiquetas'
+import { LogisticasObjetos } from './entities/logisticasObjetos'
+import { LogisticasServicos } from './entities/logisticasServicos'
+import { NaturezasDeOperacoes } from './entities/naturezasDeOperacoes'
+import { Nfces } from './entities/nfces'
+import { Nfes } from './entities/nfes'
+import { Nfses } from './entities/nfses'
+import { Notificacoes } from './entities/notificacoes'
+import { PedidosCompras } from './entities/pedidosCompras'
+import { PedidosVendas } from './entities/pedidosVendas'
+import { Produtos } from './entities/produtos'
+import { ProdutosEstruturas } from './entities/produtosEstruturas'
+import { ProdutosFornecedores } from './entities/produtosFornecedores'
+import { ProdutosLojas } from './entities/produtosLojas'
+import { ProdutosVariacoes } from './entities/produtosVariacoes'
+import { Situacoes } from './entities/situacoes'
+import { SituacoesModulos } from './entities/situacoesModulos'
+import { SituacoesTransicoes } from './entities/situacoesTransicoes'
+import { Usuarios } from './entities/usuarios'
+import { Vendedores } from './entities/vendedores'
+import { Newable } from './helpers/types/newable.type'
+import { getRepository } from './providers/ioc'
+import { IBlingRepository } from './repositories/bling.repository.interface'
+
+/**
+ * Módulo conector à API do Bling.
+ *
+ * @class
+ * @example
+ * // Constrói um novo conector
+ * const accessToken = 'sua-api-key'
+ * const bling = new Bling(accessToken)
+ */
+export default class Bling {
+ #repository: IBlingRepository
+ #modules: Record
+
+ /**
+ * Constrói o objeto.
+ *
+ * @param accessToken O token de acesso à API do Bling.
+ */
+ constructor(accessToken: string) {
+ this.#repository = getRepository(accessToken)
+ this.#modules = {}
+ }
+
+ /**
+ * Obtém um módulo através de sua assinatura (seguindo o _pattern_ `Instance`).
+ *
+ * @param {Newable} EntityClass A entidade desejada.
+ *
+ * @returns {T} A instância da entidade.
+ */
+ private getModule(EntityClass: Newable): T {
+ if (!this.#modules[EntityClass.name]) {
+ this.#modules[EntityClass.name] = new EntityClass(this.#repository)
}
- if (typeof options.raw !== 'boolean') {
- throw createError({
- name: 'BlingModuleError',
- message:
- 'The raw attribute must be a boolean to configure the Bling connection.',
- status: '500',
- data: {
- raw: options.raw
- },
- code: 'ERR_WRONG_BLING_RAW_ATTR'
- })
- }
-
- this.#raw = options.raw
-
- const api = axios.create({
- baseURL: 'https://bling.com.br/Api/v2'
- })
-
- api.interceptors.request.use((config) => {
- if (
- config.method &&
- ['POST', 'PUT', 'post', 'put'].includes(config.method)
- ) {
- config.data = qs.stringify({
- apikey: apiKey,
- ...config.data
- })
- }
-
- config.params = {
- apikey: apiKey,
- ...config.params
- }
-
- return config
- })
-
- this.#api = api
- }
-
- static create (apiKey: string, options: { raw: boolean } = { raw: false }) {
- return new this(apiKey, options)
- }
-
- public borderos () {
- if (!this.#borderos) {
- this.#borderos = Borderos(this.#api, this.#raw)
- }
- return this.#borderos
- }
-
- public customizedFields () {
- if (!this.#customizedFields) {
- this.#customizedFields = CustomizedField(this.#api, this.#raw)
- }
- return this.#customizedFields
- }
-
- public camposCustomizados () {
- return this.customizedFields()
- }
-
- public categories () {
- if (!this.#categories) {
- this.#categories = Categories(this.#api, this.#raw)
- }
- return this.#categories
- }
-
- public categorias () {
- return this.categories()
- }
-
- public shopCategories () {
- if (!this.#shopCategories) {
- this.#shopCategories = ShopCategories(this.#api, this.#raw)
- }
- return this.#shopCategories
+ return this.#modules[EntityClass.name] as T
}
-
- public categoriasLoja () {
- return this.shopCategories()
- }
-
- public contacts () {
- if (!this.#contacts) {
- this.#contacts = Contacts(this.#api, this.#raw)
- }
- return this.#contacts
- }
-
- public contatos () {
- return this.contacts()
- }
-
- public billsToPay () {
- if (!this.#billsToPay) {
- this.#billsToPay = BillsToPay(this.#api, this.#raw)
- }
- return this.#billsToPay
- }
-
- public contasAPagar () {
- return this.billsToPay()
- }
-
- public billsToReceive () {
- if (!this.#billsToReceive) {
- this.#billsToReceive = BillsToReceive(this.#api, this.#raw)
- }
- return this.#billsToReceive
- }
-
- public contasAReceber () {
- return this.billsToReceive()
- }
-
- public contracts () {
- if (!this.#contracts) {
- this.#contracts = Contracts(this.#api, this.#raw)
- }
- return this.#contracts
- }
-
- public contratos () {
- return this.contracts()
- }
-
- public ctes () {
- if (!this.#ctes) {
- this.#ctes = Ctes(this.#api, this.#raw)
- }
- return this.#ctes
- }
-
- public deposits () {
- if (!this.#deposits) {
- this.#deposits = Deposits(this.#api, this.#raw)
- }
- return this.#deposits
- }
-
- public depositos () {
- return this.deposits()
- }
-
- public paymentMethods () {
- if (!this.#paymentMethods) {
- this.#paymentMethods = PaymentMethods(this.#api, this.#raw)
- }
- return this.#paymentMethods
- }
-
- public formasDePagamento () {
- return this.paymentMethods()
- }
-
- public productGroups () {
- if (!this.#productGroups) {
- this.#productGroups = ProductGroups(this.#api, this.#raw)
- }
- return this.#productGroups
- }
-
- public grupoDeProdutos () {
- return this.productGroups()
- }
-
- public nfces () {
- if (!this.#nfces) {
- this.#nfces = Nfces(this.#api, this.#raw)
- }
- return this.#nfces
- }
-
- public invoices () {
- if (!this.#invoices) {
- this.#invoices = Invoices(this.#api, this.#raw)
- }
- return this.#invoices
- }
-
- public notasFiscais () {
- return this.invoices()
- }
-
- public serviceInvoices () {
- if (!this.#serviceInvoices) {
- this.#serviceInvoices = ServiceInvoices(this.#api, this.#raw)
- }
- return this.#serviceInvoices
- }
-
- public notasServicos () {
- return this.serviceInvoices()
- }
-
- public orders () {
- if (!this.#orders) {
- this.#orders = Orders(this.#api, this.#raw)
- }
- return this.#orders
- }
-
- public pedidos () {
- return this.orders()
- }
-
- public purchaseOrders () {
- if (!this.#purchaseOrders) {
- this.#purchaseOrders = PurchaseOrders(this.#api, this.#raw)
- }
- return this.#purchaseOrders
- }
-
- public pedidosDeCompra () {
- return this.purchaseOrders()
- }
-
- public products () {
- if (!this.#products) {
- this.#products = Products(this.#api, this.#raw)
- }
- return this.#products
- }
-
- public produtos () {
- return this.products()
- }
-
- public commercialProposals () {
- if (!this.#commercialProposals) {
- this.#commercialProposals = CommercialProposals(this.#api, this.#raw)
- }
- return this.#commercialProposals
- }
-
- public propostasComerciais () {
- return this.commercialProposals()
+
+ /**
+ * Obtém a instância de interação com borderôs.
+ *
+ * @returns {Borderos}
+ */
+ public get borderos(): Borderos {
+ return this.getModule(Borderos)
+ }
+
+ /**
+ * Obtém a instância de interação com campos customizados.
+ *
+ * @returns {CamposCustomizados}
+ */
+ public get camposCustomizados(): CamposCustomizados {
+ return this.getModule(CamposCustomizados)
+ }
+
+ /**
+ * Obtém a instância de interação com categorias - lojas.
+ *
+ * @return {CategoriasLojas}
+ */
+ public get categoriasLojas(): CategoriasLojas {
+ return this.getModule(CategoriasLojas)
+ }
+
+ /**
+ * Obtém a instância de interação com categorias - produtos.
+ *
+ * @return {CategoriasProdutos}
+ */
+ public get categoriasProdutos(): CategoriasProdutos {
+ return this.getModule(CategoriasProdutos)
+ }
+
+ /**
+ * Obtém a instância de interação com categorias - receitas e despesas.
+ *
+ * @return {CategoriasReceitasDespesas}
+ */
+ public get categoriasReceitasDespesas(): CategoriasReceitasDespesas {
+ return this.getModule(CategoriasReceitasDespesas)
+ }
+
+ /**
+ * Obtém a instância de interação com contas a pagar.
+ *
+ * @return {ContasPagar}
+ */
+ public get contasPagar(): ContasPagar {
+ return this.getModule(ContasPagar)
+ }
+
+ /**
+ * Obtém a instância de interação com contas a receber.
+ *
+ * @return {ContasReceber}
+ */
+ public get contasReceber(): ContasReceber {
+ return this.getModule(ContasReceber)
+ }
+
+ /**
+ * Obtém a instância de interação com contas contábeis.
+ *
+ * @return {ContasContabeis}
+ */
+ public get contasContabeis(): ContasContabeis {
+ return this.getModule(ContasContabeis)
+ }
+
+ /**
+ * Obtém a instância de interação com contatos.
+ *
+ * @return {Contatos}
+ */
+ public get contatos(): Contatos {
+ return this.getModule(Contatos)
+ }
+
+ /**
+ * Obtém a instância de interação com contatos - tipos.
+ *
+ * @return {ContatosTipos}
+ */
+ public get contatosTipos(): ContatosTipos {
+ return this.getModule(ContatosTipos)
+ }
+
+ /**
+ * Obtém a instância de interação com contratos.
+ *
+ * @return {Contratos}
+ */
+ public get contratos(): Contratos {
+ return this.getModule(Contratos)
+ }
+
+ /**
+ * Obtém a instância de interação com depósitos.
+ *
+ * @return {Depositos}
+ */
+ public get depositos(): Depositos {
+ return this.getModule(Depositos)
+ }
+
+ /**
+ * Obtém a instância de interação com empresas.
+ *
+ * @return {Empresas}
+ */
+ public get empresas(): Empresas {
+ return this.getModule(Empresas)
+ }
+
+ /**
+ * Obtém a instância de interação com estoques.
+ *
+ * @return {Estoques}
+ */
+ public get estoques(): Estoques {
+ return this.getModule(Estoques)
+ }
+
+ /**
+ * Obtém a instância de interação com formas de pagamento.
+ *
+ * @return {FormasDePagamento}
+ */
+ public get formasDePagamento(): FormasDePagamento {
+ return this.getModule(FormasDePagamento)
+ }
+
+ /**
+ * Obtém a instância de interação com homologação.
+ *
+ * @return {Homologacao}
+ */
+ public get homologacao(): Homologacao {
+ return this.getModule(Homologacao)
+ }
+
+ /**
+ * Obtém a instância de interação com logísticas.
+ *
+ * @return {Logisticas}
+ */
+ public get logisticas(): Logisticas {
+ return this.getModule(Logisticas)
+ }
+
+ /**
+ * Obtém a instância de interação com logísticas - etiquetas.
+ *
+ * @return {LogisticasEtiquetas}
+ */
+ public get logisticasEtiquetas(): LogisticasEtiquetas {
+ return this.getModule(LogisticasEtiquetas)
+ }
+
+ /**
+ * Obtém a instância de interação com logísticas - objetos.
+ *
+ * @return {LogisticasObjetos}
+ */
+ public get logisticasObjetos(): LogisticasObjetos {
+ return this.getModule(LogisticasObjetos)
+ }
+
+ /**
+ * Obtém a instância de interação com logísticas - serviços.
+ *
+ * @return {LogisticasServicos}
+ */
+ public get logisticasServicos(): LogisticasServicos {
+ return this.getModule(LogisticasServicos)
+ }
+
+ /**
+ * Obtém a instância de interação com naturezas de operações.
+ *
+ * @return {NaturezasDeOperacoes}
+ */
+ public get naturezasDeOperacoes(): NaturezasDeOperacoes {
+ return this.getModule(NaturezasDeOperacoes)
+ }
+
+ /**
+ * Obtém a instância de interação com notas fiscals de consumidor eletrônicas.
+ *
+ * @return {Nfces}
+ */
+ public get nfces(): Nfces {
+ return this.getModule(Nfces)
+ }
+
+ /**
+ * Obtém a instância de interação com notas fiscals de serviço eletrônicas.
+ *
+ * @return {Nfses}
+ */
+ public get nfses(): Nfses {
+ return this.getModule(Nfses)
+ }
+
+ /**
+ * Obtém a instância de interação com notas fiscals de serviço eletrônicas.
+ *
+ * @return {Nfes}
+ */
+ public get nfes(): Nfes {
+ return this.getModule(Nfes)
+ }
+
+ /**
+ * Obtém a instância de interação com notificações.
+ *
+ * @return {Notificacoes}
+ */
+ public get notificacoes(): Notificacoes {
+ return this.getModule(Notificacoes)
+ }
+
+ /**
+ * Obtém a instância de interação com pedidos - compras.
+ *
+ * @return {PedidosCompras}
+ */
+ public get pedidosCompras(): PedidosCompras {
+ return this.getModule(PedidosCompras)
+ }
+
+ /**
+ * Obtém a instância de interação com pedidos - vendas.
+ *
+ * @return {PedidosVendas}
+ */
+ public get pedidosVendas(): PedidosVendas {
+ return this.getModule(PedidosVendas)
+ }
+
+ /**
+ * Obtém a instância de interação com produtos.
+ *
+ * @return {Produtos}
+ */
+ public get produtos(): Produtos {
+ return this.getModule(Produtos)
+ }
+
+ /**
+ * Obtém a instância de interação com produtos - estruturas.
+ *
+ * @return {ProdutosEstruturas}
+ */
+ public get produtosEstruturas(): ProdutosEstruturas {
+ return this.getModule(ProdutosEstruturas)
+ }
+
+ /**
+ *
+ * Obtém a instância de interação com produtos - fornecedores.
+ *
+ * @return {ProdutosFornecedores}
+ */
+ public get produtosFornecedores(): ProdutosFornecedores {
+ return this.getModule(ProdutosFornecedores)
+ }
+
+ /**
+ *
+ * Obtém a instância de interação com produtos - fornecedores.
+ *
+ * @return {ProdutosLojas}
+ */
+ public get produtosLojas(): ProdutosLojas {
+ return this.getModule(ProdutosLojas)
+ }
+
+ /**
+ *
+ * Obtém a instância de interação com produtos - variações.
+ *
+ * @return {ProdutosVariacoes}
+ */
+ public get produtosVariacoes(): ProdutosVariacoes {
+ return this.getModule(ProdutosVariacoes)
+ }
+
+ /**
+ *
+ * Obtém a instância de interação com situações.
+ *
+ * @return {Situacoes}
+ */
+ public get situacoes(): Situacoes {
+ return this.getModule(Situacoes)
+ }
+
+ /**
+ *
+ * Obtém a instância de interação com situações - módulos.
+ *
+ * @return {SituacoesModulos}
+ */
+ public get situacoesModulos(): SituacoesModulos {
+ return this.getModule(SituacoesModulos)
+ }
+
+ /**
+ *
+ * Obtém a instância de interação com situações - transições.
+ *
+ * @return {SituacoesTransicoes}
+ */
+ public get situacoesTransicoes(): SituacoesTransicoes {
+ return this.getModule(SituacoesTransicoes)
+ }
+
+ /**
+ *
+ * Obtém a instância de interação com usuários.
+ *
+ * @return {Usuarios}
+ */
+ public get usuarios(): Usuarios {
+ return this.getModule(Usuarios)
+ }
+
+ /**
+ *
+ * Obtém a instância de interação com vendedores.
+ *
+ * @return {Vendedores}
+ */
+ public get vendedores(): Vendedores {
+ return this.getModule(Vendedores)
}
}
diff --git a/src/core/functions/all.ts b/src/core/functions/all.ts
deleted file mode 100644
index b16fe34..0000000
--- a/src/core/functions/all.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import {
- IPluralResponse,
- IPluralEntity,
- ISingularEntity,
- IApiError,
- IPluralError
-} from '../interfaces/method'
-
-import Method from '../template/method'
-import createError from '../helpers/createError'
-import packErrorsToJsonApi from '../helpers/packErrorsToJsonApi'
-import handleApiError from '../helpers/handleApiError'
-
-export default class All extends Method {
- /**
- * Retrieve all entities from the given endpoint.
- * @private
- * @access private
- * @async
- * @param params The params to filter or enhance the response.
- * @param infos Parameters to enhance the response data.
- * @param filters The filters used for the query.
- * @param options The options object.
- * @param raw Return either raw data from Bling or beautified processed data.
- * @param page The response page with pagination is desired.
- */
-
- public async all(options?: {
- params?: {
- filters?: IFilters
- infos?: IInfo
- }
- raw?: false
- page?: number
- }): Promise
-
- public async all(options?: {
- params?: {
- filters?: IFilters
- infos?: IInfo
- }
- raw: true
- page?: number
- }): Promise>
-
- public async all (options?: {
- params?: {
- filters?: IFilters
- infos?: IInfo
- }
- raw?: boolean
- page?: number
- }): Promise> {
- const entities: IEntityResponse[] = []
-
- const params: { filters?: string; [key: string]: unknown } = {}
-
- const endpoint = this.endpoint || this.pluralName
- const raw = options && options.raw !== undefined ? options.raw : this.raw
-
- let hasMore = true
- let reqCount = 0
- let page = 1
-
- let isSinglePage = false
-
- if (options) {
- if (options.params) {
- if (options.params.filters) {
- const typedParams = options.params.filters as unknown as {
- [key: string]: string
- }
- const filters = Object.keys(options.params.filters)
- .map((key: string) =>
- typedParams[key] ? `${key}[${typedParams[key]}]` : null
- )
- .filter((item) => !!item)
- .join(';')
-
- params.filters = filters
- }
-
- if (options.params.infos) {
- for (const infoKey in options.params.infos) {
- params[infoKey] = options.params.infos[infoKey]
- }
- }
- }
-
- if (options.page) {
- isSinglePage = true
- page = options.page
- }
- }
-
- while (hasMore) {
- const response = await this.api
- .get(`/${endpoint}/page=${page}/json`, {
- params
- })
- .catch((err: IApiError) => {
- const errorData = {
- name: 'BlingRequestError',
- message: `Error on all method during request call: ${err.message}`,
- status: String(err.response?.status) || '400',
- code: 'ERR_GET_REQUEST_FAILURE'
- }
-
- const rawData = err.response?.data as IPluralResponse
-
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- })
-
- const rawData = response.data as IPluralResponse
- const responseData = rawData.retorno
-
- if (responseData.erros) {
- hasMore = false
-
- const pluralError = responseData as IPluralError
- const jsonApiErrorObj = packErrorsToJsonApi(pluralError)
-
- // If there is an error different than 'not found'
- if (jsonApiErrorObj.errors.some((err) => err.code !== '14')) {
- const errorData = {
- name: 'BlingRequestError',
- message: 'Error on all method after request call',
- status: '400',
- code: 'ERR_ALL_METHOD_FAILURE'
- }
-
- if (raw) {
- throw createError({
- ...errorData,
- data: rawData
- })
- } else {
- throw createError({
- ...errorData,
- data: jsonApiErrorObj
- })
- }
- }
- } else {
- const rawNewEntities = responseData as IPluralEntity
-
- const newEntities = rawNewEntities[
- this.pluralName
- ] as ISingularEntity[]
-
- const singularEntities = newEntities.map(
- (item) => item[this.singularName]
- )
- for (const entity of singularEntities) {
- entities.push(entity)
- }
- }
-
- if (isSinglePage) {
- break
- }
-
- page++
-
- reqCount++
- if (reqCount === 3) {
- const sleep = new Promise((resolve) => {
- setTimeout(resolve, 1000)
- })
-
- await sleep
-
- reqCount = 0
- }
- }
-
- if (raw) {
- return {
- retorno: {
- [this.pluralName]: entities.map((entity) => ({
- [this.singularName]: entity
- }))
- }
- }
- } else {
- return entities
- }
- }
-}
diff --git a/src/core/functions/create.ts b/src/core/functions/create.ts
deleted file mode 100644
index 24cb7a6..0000000
--- a/src/core/functions/create.ts
+++ /dev/null
@@ -1,191 +0,0 @@
-import xml2js from 'xml2js'
-
-import {
- IPluralResponse,
- IPluralEntity,
- ISingularEntity,
- IApiError
-} from '../interfaces/method'
-
-import Method from '../template/method'
-
-import createError from '../helpers/createError'
-import handleApiError from '../helpers/handleApiError'
-import convertArraysToObj from '../helpers/convertArraysToObj'
-
-export default class Create extends Method {
- /**
- * Create an entity on the given endpoint.
- * @protected
- * @access protected
- * @async
- * @param data The data for the entity to be created.
- * @param options The options object to define the response structure.
- * @param raw Return either raw data from Bling or beautified processed data.
- * @returns The created entity.
- */
- public async create(
- data: IEntity,
- options?: {
- raw?: false
- },
- ...restData: unknown[]
- ): Promise
-
- public async create(
- data: IEntity,
- options?: {
- raw: true
- },
- ...restData: unknown[]
- ): Promise>
-
- public async create (
- data: IEntity,
- options?: {
- raw?: boolean
- },
- ...restData: unknown[]
- ): Promise> {
- if (!data || typeof data !== 'object' || Object.keys(data).length === 0) {
- throw createError({
- name: 'BlingCreateError',
- message: 'The "data" argument must be a not empty object',
- status: '500',
- data,
- code: 'ERR_INCORRECT_CREATE_DATA'
- })
- }
-
- const xmlBuilder = new xml2js.Builder({ rootName: this.singularName })
- const xml = xmlBuilder.buildObject(convertArraysToObj(data))
-
- const params = {
- xml,
- ...restData
- }
-
- const endpoint = this.endpoint || this.singularName
- const raw = options && options.raw !== undefined ? options.raw : this.raw
-
- const response = await this.api
- .post(`/${endpoint}/json`, params)
- .catch((err: IApiError) => {
- const errorData = {
- name: 'BlingRequestError',
- message: `Error on create method during request call: ${err.message}`,
- status: String(err.response?.status) || '400',
- code: err.code || 'ERR_POST_REQUEST_FAILURE'
- }
-
- const rawData = err.response?.data as
- | IPluralResponse
- | ''
-
- if (rawData === '') {
- console.log(err.response?.data)
- throw createError({
- ...errorData,
- data: {
- errors: [
- {
- title: 'Empty return',
- detail: 'The request has gotten an empty return.'
- }
- ]
- }
- })
- } else {
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- }
- })
-
- const rawData = response.data as IPluralResponse
- const responseData = rawData.retorno
-
- if (responseData.erros) {
- /**
- * It can return as (most of the cases)
- * {
- * retorno: {
- * erros: {
- * cod: string,
- * msg: string
- * }[]
- * }
- * }
- *
- * or (paymentMethod case)
- * {
- * retorno: {
- * [cod: string]: string
- * }[]
- * }
- *
- * or (also paymentMethod case)
- * {
- * retorno: string[]
- * }
- * */
- const errorData = {
- name: 'BlingRequestError',
- message: 'Error on create method after request call',
- status: '400',
- code: 'ERR_CREATE_METHOD_FAILURE'
- }
-
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- } else {
- if (raw) {
- return rawData
- } else {
- const rawResponse = responseData as IPluralEntity
-
- if (Array.isArray(rawResponse[this.pluralName])) {
- /**
- * It can return as (most of the cases)
- * {
- * retorno: {
- * [pluralName]: {
- * [singularName]: IEntityResponse
- * }[]
- * }
- * }
- *
- * or (paymentMethod case)
- * {
- * retorno: {
- * [pluralName]: IEntityResponse[]
- * }
- * }
- * */
- const rawEntity = rawResponse[this.pluralName] as
- | ISingularEntity[]
- | IEntityResponse[]
-
- if (Object.keys(rawEntity[0]).length === 1) {
- const arrRawReturn = rawEntity as ISingularEntity[]
- return arrRawReturn.map(
- (entity) => entity[this.singularName]
- ) as IEntityResponse[]
- } else {
- return rawEntity as IEntityResponse[]
- }
- } else {
- const rawEntity = rawResponse[
- this.pluralName
- ] as ISingularEntity
- return [rawEntity[this.singularName]]
- }
- }
- }
- }
-}
diff --git a/src/core/functions/delete.ts b/src/core/functions/delete.ts
deleted file mode 100644
index 2bc55b7..0000000
--- a/src/core/functions/delete.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-import {
- IPluralResponse,
- IPluralEntity,
- ISingularEntity,
- IApiError
-} from '../interfaces/method'
-
-import Method from '../template/method'
-import createError from '../helpers/createError'
-import handleApiError from '../helpers/handleApiError'
-
-export default class Find extends Method {
- /**
- * Delete an entity on the given endpoint.
- * @protected
- * @access protected
- * @async
- * @param id The entity code or id.
- * @param options The options object.
- * @param raw Return either raw data from Bling or beautified processed data.
- * @returns The deleted entity.
- */
- public async delete(
- id: number | string,
- options?: {
- raw?: false
- }
- ): Promise
-
- public async delete(
- id: number | string,
- options?: {
- raw: true
- }
- ): Promise>
-
- public async delete (
- id: number | string,
- options?: {
- raw?: boolean
- }
- ): Promise> {
- if (!id || typeof id === 'object' || Array.isArray(id)) {
- throw createError({
- name: 'BlingDeleteError',
- message: 'The "id" argument must be a number or string',
- status: '500',
- data: { id },
- code: 'ERR_INCORRECT_DELETE_ID'
- })
- }
-
- const endpoint = this.endpoint || this.singularName
- const raw = options && options.raw !== undefined ? options.raw : this.raw
-
- const response = await this.api
- .delete(`/${endpoint}/${id}/json`)
- .catch((err: IApiError) => {
- const errorData = {
- name: 'BlingRequestError',
- message: `Error on delete method during request call: ${err.message}`,
- status: String(err.response?.status) || '400',
- code: err.code || 'ERR_DELETE_REQUEST_FAILURE'
- }
-
- const rawData = err.response?.data as IPluralResponse
-
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- })
-
- const rawData = response.data as IPluralResponse
- const responseData = rawData.retorno
-
- if (responseData.erros) {
- const errorData = {
- name: 'BlingRequestError',
- message: 'Error on delete method after request call',
- status: '400',
- code: 'ERR_DELETE_METHOD_FAILURE'
- }
-
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- } else {
- if (raw) {
- return rawData
- } else {
- const rawResponse = responseData as IPluralEntity
- const rawEntity = rawResponse[
- this.pluralName
- ] as ISingularEntity[]
- return rawEntity[0][this.singularName]
- }
- }
- }
-}
diff --git a/src/core/functions/find.ts b/src/core/functions/find.ts
deleted file mode 100644
index 84807c7..0000000
--- a/src/core/functions/find.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-import {
- IPluralResponse,
- IPluralEntity,
- ISingularEntity,
- IApiError
-} from '../interfaces/method'
-
-import Method from '../template/method'
-import createError from '../helpers/createError'
-import handleApiError from '../helpers/handleApiError'
-
-export default class Find extends Method {
- /**
- * Retrieve one entity from the given endpoint.
- * @protected
- * @access protected
- * @async
- * @param id The entity id.
- * @param options The options object.
- * @param infos Parameters to enhance the response data.
- * @param params The params to filter or enhance the response.
- * @param raw Return either raw data from Bling or beautified processed data.
- * @returns The found entity.
- */
- public async find(
- id: number | string,
- options?: { params?: IInfos; raw?: false }
- ): Promise
-
- public async find(
- id: number | string,
- options?: { params?: IInfos; raw: true }
- ): Promise>
-
- public async find (
- id: number | string,
- options?: {
- params?: IInfos
- raw?: boolean
- }
- ): Promise<
- IEntityResponse | IEntityResponse[] | IPluralResponse
- > {
- if (!id) {
- throw createError({
- name: 'BlingFindError',
- message: 'The "id" argument must be a number or string.',
- status: '500',
- data: { id },
- code: 'ERR_INCORRECT_FIND_ID'
- })
- }
-
- const endpoint = this.endpoint || this.singularName
- const raw = options && options.raw !== undefined ? options.raw : this.raw
-
- const response = await this.api
- .get(`/${endpoint}/${id}/json`, {
- params: options && options.params
- })
- .catch((err: IApiError) => {
- const errorData = {
- name: 'BlingRequestError',
- message: `Error on find method during request call: ${err.message}`,
- status: String(err.response?.status) || '400',
- code: 'ERR_GET_REQUEST_FAILURE'
- }
-
- const rawData = err.response?.data as IPluralResponse
-
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- })
-
- const rawData = response.data as IPluralResponse
- const responseData = rawData.retorno
-
- if (responseData.erros) {
- const errorData = {
- name: 'BlingRequestError',
- message: 'Error on find method after request call',
- status: String(response.status),
- code: 'ERR_FIND_METHOD_FAILURE'
- }
-
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- } else {
- if (raw) {
- return rawData
- } else {
- const rawResponse = responseData as IPluralEntity
- const rawEntity = rawResponse[
- this.pluralName
- ] as ISingularEntity[]
-
- if (rawEntity.length === 1) {
- return rawEntity[0][this.singularName]
- } else {
- return rawEntity.map((entity) => entity[this.singularName])
- }
- }
- }
- }
-}
diff --git a/src/core/functions/findBy.ts b/src/core/functions/findBy.ts
deleted file mode 100644
index fe50d3c..0000000
--- a/src/core/functions/findBy.ts
+++ /dev/null
@@ -1,98 +0,0 @@
-import { IPluralResponse } from '../interfaces/method'
-
-import All from './all'
-import Method from '../template/method'
-import createError from '../helpers/createError'
-
-export default class FindBy extends Method {
- /**
- * Retrieve entities that match a certain filter.
- * @private
- * @access private
- * @async
- * @param params The params to filter or enhance the response.
- * @param filters The filters used for the query.
- * @param infos Parameters to enhance the response data.
- * @param options The options object.
- * @param raw Return either raw data from Bling or beautified processed data.
- * @param page The response page with pagination is desired.
- */
- public async findBy(
- params: {
- filters: IFilters
- infos?: IInfo
- },
- options?: {
- raw?: false
- page?: number
- }
- ): Promise
-
- public async findBy(
- params: {
- filters: IFilters
- infos?: IInfo
- },
- options?: {
- raw: true
- page?: number
- }
- ): Promise>
-
- public async findBy (
- params: {
- filters: IFilters
- infos?: IInfo
- },
- options?: {
- raw?: boolean
- page?: number
- }
- ): Promise> {
- if (!params) {
- throw createError({
- name: 'BlingFindByError',
- message: 'No options passed to `.findBy()` method',
- status: '500',
- data: { params },
- code: 'ERR_INCORRECT_FINDBY_OPTIONS'
- })
- }
-
- if (!params.filters) {
- throw createError({
- name: 'BlingFindByError',
- message: 'No filters passed to `.findBy()` method',
- status: '500',
- data: { params },
- code: 'ERR_INCORRECT_FINDBY_OPTION_FILTERS'
- })
- }
-
- const raw = options && options.raw !== undefined ? options.raw : this.raw
-
- const config = {
- api: this.api,
- raw,
- endpoint: this.endpoint,
- singularName: this.singularName,
- pluralName: this.pluralName
- }
-
- const allEntity = new All(config)
-
- // @TODO: deal with interfaces problems to reuse code properly
- if (raw) {
- return await allEntity.all({
- params,
- raw: true,
- page: options && options.page
- })
- } else {
- return await allEntity.all({
- params,
- page: options && options.page
- })
- }
- }
-}
diff --git a/src/core/functions/update.ts b/src/core/functions/update.ts
deleted file mode 100644
index 7b8460f..0000000
--- a/src/core/functions/update.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import xml2js from 'xml2js'
-
-import {
- IPluralResponse,
- IPluralEntity,
- ISingularEntity,
- IApiError
-} from '../interfaces/method'
-
-import Method from '../template/method'
-
-import createError from '../helpers/createError'
-import handleApiError from '../helpers/handleApiError'
-import convertArraysToObj from '../helpers/convertArraysToObj'
-
-export default class Update extends Method {
- /**
- * Update an entity on the given endpoint.
- * @protected
- * @access protected
- * @async
- * @param id The entity code or id.
- * @param data The data for the entity to be updated.
- * @param options The options object.
- * @param raw Return either raw data from Bling or beautified processed data.
- * @return The updated entity.
- */
- public async update(
- id: number | string,
- data: IEntity,
- options?: {
- raw?: false
- }
- ): Promise
-
- public async update(
- id: number | string,
- data: IEntity,
- options?: {
- raw: true
- }
- ): Promise>
-
- public async update (
- id: number | string,
- data: IEntity,
- options?: {
- raw?: boolean
- }
- ): Promise> {
- if (!data || typeof data !== 'object' || Object.keys(data).length === 0) {
- throw createError({
- name: 'BlingUpdateError',
- message: 'The "data" argument must be a not empty object',
- status: '500',
- data,
- code: 'ERR_INCORRECT_UPDATE_DATA'
- })
- }
-
- if (!id || typeof id === 'object' || Array.isArray(id)) {
- throw createError({
- name: 'BlingUpdateError',
- message: 'The "id" argument must be a number or string',
- status: '500',
- data: { id },
- code: 'ERR_INCORRECT_UPDATE_ID'
- })
- }
-
- const xmlBuilder = new xml2js.Builder({ rootName: this.singularName })
- const xml = xmlBuilder.buildObject(convertArraysToObj(data))
-
- const params = {
- xml
- }
-
- const endpoint = this.endpoint || this.singularName
- const raw = options && options.raw !== undefined ? options.raw : this.raw
-
- const response = await this.api
- .put(`/${endpoint}/${id}/json`, params)
- .catch((err: IApiError) => {
- const errorData = {
- name: 'BlingRequestError',
- message: `Error on update method during request call: ${err.message}`,
- status: String(err.response?.status) || '400',
- code: err.code || 'ERR_PUT_REQUEST_FAILURE'
- }
-
- const rawData = err.response?.data as IPluralResponse
-
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- })
-
- const rawData = response.data as IPluralResponse
- const responseData = rawData.retorno
-
- if (responseData.erros) {
- const errorData = {
- name: 'BlingRequestError',
- message: 'Error on update method after request call',
- status: '400',
- code: 'ERR_UPDATE_METHOD_FAILURE'
- }
-
- return handleApiError({
- rawData,
- errorData,
- raw
- })
- } else {
- if (raw) {
- return rawData
- } else {
- const rawResponse = responseData as IPluralEntity
-
- if (Array.isArray(rawResponse[this.pluralName])) {
- const rawEntity = rawResponse[
- this.pluralName
- ] as ISingularEntity[]
- return rawEntity[0][this.singularName]
- } else {
- const rawEntity = rawResponse[
- this.pluralName
- ] as ISingularEntity
- return rawEntity[this.singularName]
- }
- }
- }
- }
-}
diff --git a/src/core/helpers/convertArraysToObj.ts b/src/core/helpers/convertArraysToObj.ts
deleted file mode 100644
index 1c12118..0000000
--- a/src/core/helpers/convertArraysToObj.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-const convertArraysToObj = (data: any) => {
- const processedData = {} as Record
-
- for (const key in data) {
- const value = data[key]
-
- if (Array.isArray(value)) {
- const firstElem = value[0]
-
- if (typeof firstElem === 'object') {
- const attr = Object.keys(firstElem)[0]
-
- const newElems = { [attr]: value.map((item) => item[attr]) }
- processedData[key] = newElems
- } else {
- processedData[key] = value
- }
- } else if (value && typeof value === 'object') {
- processedData[key] = convertArraysToObj(value)
- } else if (value !== null && value !== undefined && value !== '') {
- processedData[key] = value
- }
- }
-
- return processedData
-}
-
-export default convertArraysToObj
diff --git a/src/core/helpers/createError.ts b/src/core/helpers/createError.ts
deleted file mode 100644
index 948dca1..0000000
--- a/src/core/helpers/createError.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-export interface IBlingErrorArgs {
- name: string
- message: string
- status: string
- data?: unknown
- code: string
-}
-
-export interface IBlingError extends Error {
- name: string
- status: string
- code: string
- data?: unknown
- toJSON: () => {
- name: string
- message: string
- code: string
- data?: unknown
- stack?: string
- }
-}
-
-/**
- * Create an Error with the specified message, config, error code and status.
- *
- * @param {string} name The error name, default to 'BlingError'.
- * @param {string} message The error message.
- * @param {string} status The error status (for example, '500').
- * @param {unknown} data The error data (for example, the API call response).
- * @param {string} code The error code (for example, 'E_CONN_ABORTED').
- * @returns {IBlingError} The created error.
- */
-export default function createError (args: IBlingErrorArgs) {
- const rawError = new Error(args.message)
-
- const error: IBlingError = {
- ...rawError,
- ...args,
- toJSON: () => {
- return {
- ...args,
- stack: rawError.stack
- }
- }
- }
-
- return error
-}
diff --git a/src/core/helpers/handleApiError.ts b/src/core/helpers/handleApiError.ts
deleted file mode 100644
index 5391ad2..0000000
--- a/src/core/helpers/handleApiError.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { IPluralResponse, IPluralError } from '../interfaces/method'
-
-import packErrorsToJsonApi from './packErrorsToJsonApi'
-import createError, { IBlingErrorArgs } from './createError'
-
-export default (args: {
- rawData: IPluralResponse
- errorData: IBlingErrorArgs
- raw?: boolean
-}) => {
- const pluralError = args.rawData.retorno as IPluralError
-
- if (args.raw) {
- throw createError({
- ...args.errorData,
- data: args.rawData || null
- })
- } else {
- const jsonApiErrorArr = packErrorsToJsonApi(pluralError)
-
- throw createError({
- ...args.errorData,
- data: jsonApiErrorArr
- })
- }
-}
diff --git a/src/core/helpers/packErrorsToJsonApi.ts b/src/core/helpers/packErrorsToJsonApi.ts
deleted file mode 100644
index 4b16203..0000000
--- a/src/core/helpers/packErrorsToJsonApi.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-import {
- IPluralError,
- ISingularError,
- IShortenedError
-} from '../interfaces/method'
-
-/**
- * Convert Bling array/object/string errors to JSON API standards. Useful for standardizing the error response.
- *
- * ######## JSON API SPECIFICATION ########
- *
- * Source: https://jsonapi.org/format/#errors
- *
- * ========================================
- *
- * An error object MAY have the following members:
- * - `id`: a unique identifier for this particular occurrence of the problem.
- * - `links`: a links object containing the following members:
- * - `about`: a link that leads to further details about this particular occurrence of the problem.
- * - `status`: the HTTP status code applicable to this problem, expressed as a string value.
- * - `code`: an application-specific error code, expressed as a string value.
- * - `title`: a short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
- * - `detail`: a human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized.
- * - `source`: an object containing references to the source of the error, optionally including any of the following members:
- * - `pointer`: a JSON Pointer [RFC6901] to the associated entity in the request document [e.g. `"/data"` for a primary data object, or `"/data/attributes/title"` for a specific attribute].
- * - `parameter`: a string indicating which URI query parameter caused the error.
- * - `meta`: a meta object containing non-standard meta-information about the error.
- */
-
-export interface JsonApiErrorObject {
- id?: string
- links?: {
- about?: string
- }
- status?: string
- code: string
- title?: string
- detail: string
- source?: {
- pointer?: string
- parameter?: string
- }
- meta?: unknown
-}
-
-export default (errArr: IPluralError) => {
- const rawErrors = errArr.erros
-
- const returnData: { errors: JsonApiErrorObject[] } = {
- errors: []
- }
-
- if (Array.isArray(rawErrors)) {
- if (typeof rawErrors[0] === 'string') {
- const definedRawErrData = rawErrors as string[]
-
- returnData.errors = definedRawErrData.map((err: string) => ({
- detail: err,
- code: '_'
- }))
- } else {
- const definedRawErrData = rawErrors as ISingularError[]
-
- returnData.errors = definedRawErrData.map((errObj: ISingularError) => ({
- detail: errObj.erro.msg,
- code: String(errObj.erro.cod)
- }))
- }
- } else {
- if (rawErrors.erro) {
- const errors = rawErrors as ISingularError
-
- returnData.errors = [
- {
- detail: errors.erro.msg,
- code: String(errors.erro.cod)
- }
- ]
- } else {
- const errors = rawErrors as IShortenedError
-
- returnData.errors = Object.keys(errors).map((errCode) => ({
- detail: errors[errCode],
- code: String(errCode)
- }))
- }
- }
-
- return returnData
-}
diff --git a/src/core/interfaces/method.ts b/src/core/interfaces/method.ts
deleted file mode 100644
index 799eb01..0000000
--- a/src/core/interfaces/method.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import {
- AxiosInstance as IAxiosInstance,
- AxiosError as IAxiosError
-} from 'axios'
-
-export type IApiInstance = IAxiosInstance
-export type IApiError = IAxiosError
-
-export interface ISingularEntity {
- [singular: string]: T
-}
-
-export interface IPluralEntity {
- [plural: string]: ISingularEntity[] | ISingularEntity | T
-}
-
-export interface ISingularError {
- erro: {
- cod: number
- msg: string
- }
-}
-
-export interface IShortenedError {
- [code: string]: string
-}
-
-export interface IPluralError {
- erros: ISingularError[] | ISingularError | IShortenedError | string[]
-}
-
-export interface IPluralResponse {
- retorno: IPluralEntity | IPluralError
-}
-
-export interface ISingularResponse {
- retorno: ISingularEntity | IPluralError
-}
diff --git a/src/core/template/method.ts b/src/core/template/method.ts
deleted file mode 100644
index 90fcbae..0000000
--- a/src/core/template/method.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { IApiInstance } from '../interfaces/method'
-
-import axios from 'axios'
-
-export default abstract class Method {
- protected api: IApiInstance = axios
- protected raw? = false
- protected endpoint?: string
- protected singularName = ''
- protected pluralName = ''
-
- constructor (args?: {
- api: IApiInstance
- raw?: boolean
- endpoint?: string
- singularName: string
- pluralName: string
- }) {
- if (args) {
- this.api = args.api
- this.endpoint = args.endpoint
- this.singularName = args.singularName
- this.pluralName = args.pluralName
-
- if (args.raw) {
- this.raw = args.raw
- }
- }
- }
-}
diff --git a/src/entities/@shared/entity.ts b/src/entities/@shared/entity.ts
new file mode 100644
index 0000000..fa56658
--- /dev/null
+++ b/src/entities/@shared/entity.ts
@@ -0,0 +1,40 @@
+import convertDateToString from '../../helpers/functions/convert-date-to-string'
+import { IBlingRepository } from '../../repositories/bling.repository.interface'
+
+/**
+ * Entidade base para o projeto.
+ */
+export abstract class Entity {
+ /** @property Repositório para conexão com o Bling. */
+ protected repository: IBlingRepository
+
+ /**
+ * Constrói o objeto.
+ *
+ * @param repository O repositório para uso da integração.
+ */
+ constructor(repository: IBlingRepository) {
+ this.repository = repository
+ }
+
+ /**
+ * Prepara um parâmetro de data para chamada do repositório.
+ *
+ * @param param Parâmetro do tipo `string`, `Date` ou `undefined`
+ *
+ * @returns {string|undefined}
+ */
+ protected prepareStringOrDateParam(
+ param?: string | Date
+ ): string | undefined {
+ if (param === undefined) {
+ return undefined
+ }
+
+ if (typeof param === 'string') {
+ return param
+ }
+
+ return convertDateToString(param)
+ }
+}
diff --git a/src/entities/@shared/interfaces/error.interface.ts b/src/entities/@shared/interfaces/error.interface.ts
new file mode 100644
index 0000000..282bc9d
--- /dev/null
+++ b/src/entities/@shared/interfaces/error.interface.ts
@@ -0,0 +1,27 @@
+interface IDefaultErrorFieldsCollectionItemResponse {
+ index: number
+ code: number
+ msg: string
+ element: string
+ namespace: string
+}
+
+export interface IDefaultErrorFieldsResponse {
+ code: number
+ msg: string
+ element: string
+ namespace: string
+ collection: IDefaultErrorFieldsCollectionItemResponse[]
+}
+
+/**
+ * Interface padrão para erros da API.
+ */
+export interface IDefaultErrorResponse {
+ error: {
+ type: string
+ message: string
+ description: string
+ fields?: IDefaultErrorFieldsResponse[]
+ }
+}
diff --git a/src/entities/@shared/types/contribuinte.type.ts b/src/entities/@shared/types/contribuinte.type.ts
new file mode 100644
index 0000000..5dcef05
--- /dev/null
+++ b/src/entities/@shared/types/contribuinte.type.ts
@@ -0,0 +1,10 @@
+/**
+ * Tipagem referente ao tipo de contribuinte do ICMS.
+ *
+ * - `1`: Contribuinte do ICMS
+ * - `2`: Contribuinte isento de ICMS
+ * - `9`: Não contribuinte.
+ */
+type IContribuinte = 1 | 2 | 9
+
+export default IContribuinte
diff --git a/src/entities/@shared/types/crt.type.ts b/src/entities/@shared/types/crt.type.ts
new file mode 100644
index 0000000..b92a6bd
--- /dev/null
+++ b/src/entities/@shared/types/crt.type.ts
@@ -0,0 +1,10 @@
+/**
+ * Tipagem referente ao código de regime tributário.
+ *
+ * - `1`: Simples Nacional
+ * - `2`: Simples Nacional - excesso de sublimite de receita bruta
+ * - `3`: Regime Normal
+ */
+type ICRT = 1 | 2 | 3
+
+export default ICRT
diff --git a/src/entities/@shared/types/frete-por-conta.type.ts b/src/entities/@shared/types/frete-por-conta.type.ts
new file mode 100644
index 0000000..2099463
--- /dev/null
+++ b/src/entities/@shared/types/frete-por-conta.type.ts
@@ -0,0 +1,13 @@
+/**
+ * Tipagem referente ao tipo de frete.
+ *
+ * - `0`: Contratação do Frete por conta do Remetente (CIF)
+ * - `1`: Contratação do Frete por conta do Destinatário (FOB)
+ * - `2`: Contratação do Frete por conta de Terceiros
+ * - `3`: Transporte Próprio por conta do Remetente
+ * - `4`: Transporte Próprio por conta do Destinatário
+ * - `9`: Sem Ocorrência de Transporte
+ */
+type IFretePorConta = 0 | 1 | 2 | 3 | 4 | 9
+
+export default IFretePorConta
diff --git a/src/entities/@shared/types/modelo-documento-referenciado.type.ts b/src/entities/@shared/types/modelo-documento-referenciado.type.ts
new file mode 100644
index 0000000..6cb20da
--- /dev/null
+++ b/src/entities/@shared/types/modelo-documento-referenciado.type.ts
@@ -0,0 +1,13 @@
+/**
+ * Tipagem referente ao modelo do documento fiscal referenciado.
+ *
+ * - `1`: Nota fiscal talão
+ * - `2`: Nota fiscal de consumidor talão
+ * - `2D`: Cupom fiscal
+ * - `4`: Nota de produtor
+ * - `55`: NF-e
+ * - `65`: NFC-e
+ */
+type IModeloDocumentoReferenciado = '1' | '2' | '2D' | '4' | '55' | '65'
+
+export default IModeloDocumentoReferenciado
diff --git a/src/entities/@shared/types/origem.type.ts b/src/entities/@shared/types/origem.type.ts
new file mode 100644
index 0000000..cf2a5d3
--- /dev/null
+++ b/src/entities/@shared/types/origem.type.ts
@@ -0,0 +1,16 @@
+/**
+ * Tipagem referente à origem de um item.
+ *
+ * - `0`: Nacional, exceto as indicadas nos códigos 3, 4, 5 e 8
+ * - `1`: Estrangeira - Importação direta, exceto a indicada no código 6
+ * - `2`: Estrangeira - Adquirida no mercado interno, exceto a indicada no código 7
+ * - `3`: Nacional, mercadoria ou bem com Conteúdo de Importação superior a 40% e inferior ou igual a 70%
+ * - `4`: Nacional, cuja produção tenha sido feita em conformidade com os processos produtivos básicos de que tratam as legislações citadas nos Ajustes
+ * - `5`: Nacional, mercadoria ou bem com Conteúdo de Importação inferior ou igual a 40%
+ * - `6`: Estrangeira - Importação direta, sem similar nacional, constante em lista da CAMEX
+ * - `7`: Estrangeira - Adquirida no mercado interno, sem similar nacional, constante em lista da CAMEX
+ * - `8`: Nacional, mercadoria ou bem com Conteúdo de Importação superior a 70%
+ */
+type IOrigem = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
+
+export default IOrigem
diff --git a/src/entities/@shared/types/situacao.type.ts b/src/entities/@shared/types/situacao.type.ts
new file mode 100644
index 0000000..765472e
--- /dev/null
+++ b/src/entities/@shared/types/situacao.type.ts
@@ -0,0 +1,9 @@
+/**
+ * Tipagem referente a uma situação.
+ *
+ * - `0`: Inativo
+ * - `1`: Ativo
+ */
+type ISituacao = 0 | 1
+
+export default ISituacao
diff --git a/src/entities/@shared/types/tipo-item.type.ts b/src/entities/@shared/types/tipo-item.type.ts
new file mode 100644
index 0000000..e445efa
--- /dev/null
+++ b/src/entities/@shared/types/tipo-item.type.ts
@@ -0,0 +1,9 @@
+/**
+ * Tipagem referente ao tipo do item de uma nota fiscal.
+ *
+ * - `P`: Produto
+ * - `S`: Serviço
+ */
+type ITipoItem = 'P' | 'S'
+
+export default ITipoItem
diff --git a/src/entities/@shared/types/tipoPessoa.type.ts b/src/entities/@shared/types/tipoPessoa.type.ts
new file mode 100644
index 0000000..70629ce
--- /dev/null
+++ b/src/entities/@shared/types/tipoPessoa.type.ts
@@ -0,0 +1,10 @@
+/**
+ * Tipagem representativa do tipo de pessoa.
+ *
+ * - `F`: Física
+ * - `J`: Jurídica
+ * - `E`: Estrangeira
+ */
+type ITipoPessoa = 'F' | 'J' | 'E'
+
+export default ITipoPessoa
diff --git a/src/entities/types/uf.ts b/src/entities/@shared/types/uf.type.ts
similarity index 69%
rename from src/entities/types/uf.ts
rename to src/entities/@shared/types/uf.type.ts
index 1003581..03f4389 100644
--- a/src/entities/types/uf.ts
+++ b/src/entities/@shared/types/uf.type.ts
@@ -1,4 +1,9 @@
-type IUFs =
+/**
+ * Tipagem representativa de uma UF.
+ *
+ * - `UX`: Exterior
+ */
+type IUF =
| 'AC'
| 'AL'
| 'AP'
@@ -26,5 +31,6 @@ type IUFs =
| 'SP'
| 'SE'
| 'TO'
+ | 'UX'
-export default IUFs
+export default IUF
diff --git a/src/entities/billsToPay.ts b/src/entities/billsToPay.ts
deleted file mode 100644
index c8250d8..0000000
--- a/src/entities/billsToPay.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import All from '../core/functions/all'
-import Find from '../core/functions/find'
-import FindBy from '../core/functions/findBy'
-import Create from '../core/functions/create'
-import Update from '../core/functions/update'
-
-export interface IBillToPay {
- dataEmissao?: string
- vencimentoOriginal?: string
- competencia?: string
- nroDocumento?: string
- valor: number
- historico?: string
- categoria?: string
- portador?: string
- idFormaPagamento?: string
- ocorrencia: {
- ocorrenciaTipo: 'U' | 'P' | 'M' | 'T' | 'S' | 'A' | 'E'
- diaVencimento?: number
- nroParcelas?: number
- diaSemanaVencimento?: 1 | 2 | 3 | 4 | 5 | 6 | 7
- }
- fornecedor: {
- nome?: string
- id?: string
- cpf_cnpj?: string
- tipoPessoa?: 'F' | 'J'
- ie_rg?: string
- endereco?: string
- numero?: string
- complemento?: string
- cidade?: string
- bairro?: string
- cep?: string
- uf?: string
- email?: string
- fone?: string
- celular?: string
- }
-}
-
-export interface IBillToPayUpdateContent {
- dataLiquidacao: string
- juros?: number
- desconto?: number
- acrescimo?: number
- tarifa?: number
-}
-
-export interface IBillToPayFilters {
- dataEmissao?: string
- dataVencimento?: string
- situacao?: 'pago' | 'cancelada' | 'aberto' | 'parcial'
- cnpj?: string
-}
-
-export type IBillToPayInfos = Record
-
-export interface IBillToPayCreateResponse {
- id: number
- nroDocumento: string
- vencimento: number
-}
-
-export interface IBillToPayResponse {
- id: string
- situacao: 'pago' | 'cancelada' | 'aberto' | 'parcial'
- dataEmissao: string
- vencimentoOriginal: string
- vencimento: string
- competencia: string
- nroDocumento?: string
- valor: string
- saldo: string
- historico?: string
- categoria?: string
- portador?: string
- pagamento:
- | {
- totalPago: number
- totalJuro: number
- totalDesconto: number
- totalAcrescimo: number
- totalTarifa: number
- data: string
- borderos: {
- bordero: {
- id: string
- conta: string
- dataPagamento: string
- valorPago: string
- valorJuro: string
- valorDesconto: string
- valorAcrescimo: string
- valorTarifa: string
- }
- }[]
- }
- | []
- ocorrencia:
- | 'Única'
- | 'Parcela'
- | 'Mensal'
- | 'Trimestral'
- | 'Semestral'
- | 'Anual'
- | 'Semanal'
- fornecedor: {
- idContato: string
- nome: string
- tipoPessoa: 'F' | 'J'
- cpf?: string
- cnpj?: string
- rg?: string
- endereco?: string
- numero?: string
- complemento?: string
- cidade?: string
- bairro?: string
- cep?: string
- uf?: string
- email?: string
- }
-}
-
-export default function BillsToPay (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'contapagar',
- pluralName: 'contaspagar'
- }
-
- return Object.assign(config, {
- all: new All().all,
- find: new Find().find,
- findBy: new FindBy()
- .findBy,
- create: new Create().create,
- update: new Update().update
- })
-}
diff --git a/src/entities/billsToReceive.ts b/src/entities/billsToReceive.ts
deleted file mode 100644
index 1742633..0000000
--- a/src/entities/billsToReceive.ts
+++ /dev/null
@@ -1,175 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import All from '../core/functions/all'
-import Find from '../core/functions/find'
-import FindBy from '../core/functions/findBy'
-import Create from '../core/functions/create'
-import Update from '../core/functions/update'
-
-export interface IBillToReceive {
- dataEmissao?: string
- vencimentoOriginal?: string
- competencia?: string
- nroDocumento?: string
- valor: number
- historico?: string
- categoria?: string
- idFormaPagamento?: string
- portador?: string
- vendedor?: string
- ocorrencia: {
- ocorrenciaTipo: 'U' | 'P' | 'M' | 'T' | 'S' | 'A' | 'E'
- diaVencimento?: number
- nroParcelas?: number
- diaSemanaVencimento?: 1 | 2 | 3 | 4 | 5 | 6 | 7
- }
- cliente: {
- nome?: string
- id?: string
- cpf_cnpj?: string
- tipoPessoa?: 'F' | 'J'
- ie_rg?: string
- endereco?: string
- numero?: string
- complemento?: string
- cidade?: string
- bairro?: string
- cep?: string
- uf?: string
- email?: string
- fone?: string
- celular?: string
- }
-}
-
-export interface IBillToReceiveUpdateContent {
- dataLiquidacao: string
- juros?: number
- desconto?: number
- acrescimo?: number
- tarifa?: number
-}
-
-export interface IBillToReceiveFilters {
- dataEmissao?: string
- dataVencimento?: string
- situacao?: 'pago' | 'cancelada' | 'aberto' | 'parcial'
- cnpj?: string
- dataPagamento?: string
-}
-
-export type IBillToReceiveInfos = Record
-
-export interface IBillToReceiveCreateResponse {
- id: number
- nroDocumento: string
- vencimento: number
-}
-
-export interface IBillToReceiveResponse {
- id: string
- situacao: 'pago' | 'cancelada' | 'aberto' | 'parcial'
- dataEmissao: string
- vencimentoOriginal: string
- vencimento: string
- competencia: string
- nroDocumento?: string
- valor: string
- saldo: string
- historico?: string
- categoria?: string
- idFormaPagamento?: string
- portador?: string
- linkBoleto: string
- vendedor?: string
- pagamento:
- | {
- totalPago: number
- totalJuro: number
- totalDesconto: number
- totalAcrescimo: number
- totalTarifa: number
- data: string
- }
- | []
- ocorrencia:
- | 'Única'
- | 'Parcela'
- | 'Mensal'
- | 'Trimestral'
- | 'Semestral'
- | 'Anual'
- | 'Semanal'
- cliente: {
- idContato: string
- nome: string
- tipoPessoa: 'F' | 'J'
- cpf?: string
- cnpj?: string
- rg?: string
- ie?: string
- endereco?: string
- numero?: string
- complemento?: string
- cidade?: string
- bairro?: string
- cep?: string
- uf?: string
- email?: string
- }
-}
-
-export default function BillsToReceive (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'contareceber',
- pluralName: 'contasreceber'
- }
-
- const create = async (
- data: IBillToReceive,
- options?: {
- raw?: boolean
- }
- ) => {
- const createMethod = new Create<
- IBillToReceive,
- IBillToReceiveCreateResponse
- >({
- ...config,
- endpoint: 'contareceber',
- singularName: 'contaReceber'
- })
-
- const raw = options && options.raw !== undefined ? options.raw : config.raw
-
- // @TODO: see how to reuse the code below
- if (options) {
- if (raw) {
- return await createMethod.create(data, { raw: true })
- } else {
- return await createMethod.create(data, { raw: false })
- }
- } else {
- return await createMethod.create(data, { raw: false })
- }
- }
-
- return Object.assign(config, {
- all: new All<
- IBillToReceiveResponse,
- IBillToReceiveFilters,
- IBillToReceiveInfos
- >().all,
- find: new Find().find,
- findBy: new FindBy<
- IBillToReceiveResponse,
- IBillToReceiveFilters,
- IBillToReceiveInfos
- >().findBy,
- create,
- update: new Update()
- .update
- })
-}
diff --git a/src/entities/borderos.ts b/src/entities/borderos.ts
deleted file mode 100644
index e4d0c27..0000000
--- a/src/entities/borderos.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import Delete from '../core/functions/delete'
-
-export interface IBorderoResponse {
- id: string
- mensagem: string
-}
-
-export default function Borderos (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'bordero',
- pluralName: 'borderos'
- }
-
- return Object.assign(config, {
- delete: new Delete().delete
- })
-}
diff --git a/src/entities/borderos/__tests__/delete-response.ts b/src/entities/borderos/__tests__/delete-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/borderos/__tests__/delete-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/borderos/__tests__/find-response.ts b/src/entities/borderos/__tests__/find-response.ts
new file mode 100644
index 0000000..5a38c95
--- /dev/null
+++ b/src/entities/borderos/__tests__/find-response.ts
@@ -0,0 +1,26 @@
+export default {
+ data: {
+ id: 12345678,
+ data: '2023-01-12',
+ historico: 'Referente ao pedido nº 12345678',
+ portador: {
+ id: 12345678
+ },
+ categoria: {
+ id: 12345678
+ },
+ pagamentos: [
+ {
+ contato: {
+ id: 12345678
+ },
+ numeroDocumento: '',
+ valorPago: 1500.75,
+ juros: 10,
+ desconto: 10,
+ acrescimo: 10,
+ tarifa: 10
+ }
+ ]
+ }
+}
diff --git a/src/entities/borderos/__tests__/index.spec.ts b/src/entities/borderos/__tests__/index.spec.ts
new file mode 100644
index 0000000..529da80
--- /dev/null
+++ b/src/entities/borderos/__tests__/index.spec.ts
@@ -0,0 +1,49 @@
+import { Chance } from 'chance'
+import { Borderos } from '../'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import deleteResponse from './delete-response'
+import findResponse from './find-response'
+
+const chance = Chance()
+
+describe('Borderôs entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: Borderos
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new Borderos(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should delete borderô successfully', async () => {
+ const idBordero = chance.natural()
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteResponse)
+
+ const response = await entity.delete({ idBordero })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'borderos',
+ id: String(idBordero)
+ })
+ expect(response).toBeNull()
+ })
+
+ it('should find borderô successfully', async () => {
+ const idBordero = chance.natural()
+ const spy = jest.spyOn(repository, 'show')
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idBordero })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'borderos',
+ id: String(idBordero)
+ })
+ expect(response).toBe(findResponse)
+ })
+})
diff --git a/src/entities/borderos/index.ts b/src/entities/borderos/index.ts
new file mode 100644
index 0000000..312ec2b
--- /dev/null
+++ b/src/entities/borderos/index.ts
@@ -0,0 +1,44 @@
+import { Entity } from '../@shared/entity'
+import { IDeleteParams } from './interfaces/delete.interface'
+import { IFindParams, IFindSuccessResponse } from './interfaces/find.interface'
+
+/**
+ * Entidade para interação com borderôs.
+ *
+ * @see https://developer.bling.com.br/referencia#/Border%C3%B4s
+ */
+export class Borderos extends Entity {
+ /**
+ * Remove um borderô.
+ *
+ * @param params Parâmetros para a deleção (somente o ID).
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Border%C3%B4s/delete_borderos__idBordero_
+ */
+ async delete(params: IDeleteParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'borderos',
+ id: String(params.idBordero)
+ })
+ }
+
+ /**
+ * Encontra um borderô.
+ *
+ * @param params Parâmetros para a busca (somente o ID).
+ *
+ * @returns {Promise} Os dados do borderô pesquisado.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Border%C3%B4s/get_borderos__idBordero_
+ */
+ async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'borderos',
+ id: String(params.idBordero)
+ })
+ }
+}
diff --git a/src/entities/borderos/interfaces/delete.interface.ts b/src/entities/borderos/interfaces/delete.interface.ts
new file mode 100644
index 0000000..c658443
--- /dev/null
+++ b/src/entities/borderos/interfaces/delete.interface.ts
@@ -0,0 +1,6 @@
+/**
+ * Parâmetros para deleção de um borderô.
+ */
+export interface IDeleteParams {
+ idBordero: number
+}
diff --git a/src/entities/borderos/interfaces/find.interface.ts b/src/entities/borderos/interfaces/find.interface.ts
new file mode 100644
index 0000000..06a68e6
--- /dev/null
+++ b/src/entities/borderos/interfaces/find.interface.ts
@@ -0,0 +1,34 @@
+/**
+ * Parâmetros para encontrar um borderô.
+ */
+export interface IFindParams {
+ idBordero: number
+}
+
+/**
+ * Interface de resposta bem sucedida ao encontrar um borderô.
+ */
+export interface IFindSuccessResponse {
+ data: {
+ id: number
+ data: string
+ historico: string
+ portador: {
+ id: number
+ }
+ categoria: {
+ id: number
+ }
+ pagamentos: {
+ contato: {
+ id: number
+ }
+ numeroDocumento: string
+ valorPago: number
+ juros: number
+ desconto: number
+ acrescimo: number
+ tarifa: number
+ }[]
+ }
+}
diff --git a/src/entities/camposCustomizados/__tests__/change-situation-response.ts b/src/entities/camposCustomizados/__tests__/change-situation-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/change-situation-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/camposCustomizados/__tests__/create-response.ts b/src/entities/camposCustomizados/__tests__/create-response.ts
new file mode 100644
index 0000000..cf17236
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/create-response.ts
@@ -0,0 +1,37 @@
+import ISituacao from '../../@shared/types/situacao.type'
+
+export default {
+ data: {
+ id: 12345678,
+ idsVinculosAgrupadores: [12345678],
+ idsOpcoes: [12345678]
+ }
+}
+
+export const createRequestBody = {
+ nome: 'Marca',
+ situacao: 1 as ISituacao,
+ placeholder: 'Informe a marca do produto',
+ obrigatorio: false,
+ opcoes: [
+ {
+ id: 12345678,
+ nome: 'Opção 1'
+ }
+ ],
+ tamanho: {
+ minimo: 1,
+ maximo: 10
+ },
+ agrupadores: [
+ {
+ id: 12345678
+ }
+ ],
+ modulo: {
+ id: 12345678
+ },
+ tipoCampo: {
+ id: 12345678
+ }
+}
diff --git a/src/entities/camposCustomizados/__tests__/delete-response.ts b/src/entities/camposCustomizados/__tests__/delete-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/delete-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/camposCustomizados/__tests__/find-by-module-response.ts b/src/entities/camposCustomizados/__tests__/find-by-module-response.ts
new file mode 100644
index 0000000..60f5ead
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/find-by-module-response.ts
@@ -0,0 +1,9 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ nome: 'Marca',
+ situacao: 1
+ }
+ ]
+}
diff --git a/src/entities/camposCustomizados/__tests__/find-response.ts b/src/entities/camposCustomizados/__tests__/find-response.ts
new file mode 100644
index 0000000..d077581
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/find-response.ts
@@ -0,0 +1,30 @@
+export default {
+ data: {
+ id: 12345678,
+ nome: 'Marca',
+ situacao: 1,
+ placeholder: 'Informe a marca do produto',
+ obrigatorio: false,
+ opcoes: [
+ {
+ id: 12345678,
+ nome: 'Opção 1'
+ }
+ ],
+ tamanho: {
+ minimo: 1,
+ maximo: 10
+ },
+ agrupadores: [
+ {
+ id: 12345678
+ }
+ ],
+ modulo: {
+ id: 12345678
+ },
+ tipoCampo: {
+ id: 12345678
+ }
+ }
+}
diff --git a/src/entities/camposCustomizados/__tests__/get-modules-response.ts b/src/entities/camposCustomizados/__tests__/get-modules-response.ts
new file mode 100644
index 0000000..ac63064
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/get-modules-response.ts
@@ -0,0 +1,17 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ nome: 'Clientes e Fornecedores',
+ modulo: 'Contatos',
+ agrupador: 'Tipo de contato',
+ permissoes: [
+ {
+ nome: 'Clientes e Fornecedores',
+ modulo: 'Contatos',
+ autorizado: true
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/entities/camposCustomizados/__tests__/get-types-response.ts b/src/entities/camposCustomizados/__tests__/get-types-response.ts
new file mode 100644
index 0000000..4eb8ece
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/get-types-response.ts
@@ -0,0 +1,9 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ nome: 'Inteiro',
+ mascara: ''
+ }
+ ]
+}
diff --git a/src/entities/camposCustomizados/__tests__/index.spec.ts b/src/entities/camposCustomizados/__tests__/index.spec.ts
new file mode 100644
index 0000000..9ee9677
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/index.spec.ts
@@ -0,0 +1,150 @@
+import { Chance } from 'chance'
+import { CamposCustomizados } from '../'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import ISituacao from '../../@shared/types/situacao.type'
+import changeSituationResponse from './change-situation-response'
+import createResponse, { createRequestBody } from './create-response'
+import deleteResponse from './delete-response'
+import findByModuleResponse from './find-by-module-response'
+import findResponse from './find-response'
+import getModulesResponse from './get-modules-response'
+import getTypesResponse from './get-types-response'
+import updateResponse, { updateRequestBody } from './update-response'
+
+const chance = Chance()
+
+describe('Campos customizados entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: CamposCustomizados
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new CamposCustomizados(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should delete successfully', async () => {
+ const idCampoCustomizado = chance.natural()
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteResponse)
+
+ const response = await entity.delete({ idCampoCustomizado })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'campos-customizados',
+ id: String(idCampoCustomizado)
+ })
+ expect(response).toBe(deleteResponse)
+ })
+
+ it('should get modules successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getModulesResponse)
+
+ const response = await entity.getModules()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'campos-customizados/modulos'
+ })
+ expect(response).toBe(getModulesResponse)
+ })
+
+ it('should get types successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getTypesResponse)
+
+ const response = await entity.getTypes()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'campos-customizados/tipos'
+ })
+ expect(response).toBe(getTypesResponse)
+ })
+
+ it('should find by module successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idModulo = chance.natural()
+ const pagina = chance.pickone([chance.natural(), undefined])
+ const limite = chance.pickone([chance.natural(), undefined])
+ repository.setResponse(findByModuleResponse)
+
+ const response = await entity.findByModule({ idModulo, pagina, limite })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'campos-customizados/modulos',
+ id: String(idModulo),
+ params: {
+ pagina,
+ limite
+ }
+ })
+ expect(response).toBe(findByModuleResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idCampoCustomizado = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idCampoCustomizado })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'campos-customizados',
+ id: String(idCampoCustomizado)
+ })
+ expect(response).toBe(findResponse)
+ })
+
+ it('should change situation successfully', async () => {
+ const spy = jest.spyOn(repository, 'update')
+ const idCampoCustomizado = chance.natural()
+ const situacao = chance.pickone([0, 1]) as ISituacao
+ repository.setResponse(changeSituationResponse)
+
+ const response = await entity.changeSituation({
+ idCampoCustomizado,
+ situacao
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'campos-customizados',
+ id: `${idCampoCustomizado}/situacoes`,
+ body: { situacao }
+ })
+ expect(response).toBe(changeSituationResponse)
+ })
+
+ it('should create successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(createResponse)
+
+ const response = await entity.create(createRequestBody)
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'campos-customizados',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'update')
+ const idCampoCustomizado = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idCampoCustomizado,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'campos-customizados',
+ id: String(idCampoCustomizado),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/camposCustomizados/__tests__/update-response.ts b/src/entities/camposCustomizados/__tests__/update-response.ts
new file mode 100644
index 0000000..b8c8e43
--- /dev/null
+++ b/src/entities/camposCustomizados/__tests__/update-response.ts
@@ -0,0 +1,31 @@
+import ISituacao from '../../@shared/types/situacao.type'
+
+export default {
+ data: {
+ id: 12345678,
+ idsVinculosAgrupadores: [12345678],
+ idsOpcoes: [12345678]
+ }
+}
+
+export const updateRequestBody = {
+ nome: 'Marca',
+ situacao: 1 as ISituacao,
+ placeholder: 'Informe a marca do produto',
+ obrigatorio: false,
+ opcoes: [
+ {
+ id: 12345678,
+ nome: 'Opção 1'
+ }
+ ],
+ tamanho: {
+ minimo: 1,
+ maximo: 10
+ },
+ agrupadores: [
+ {
+ id: 12345678
+ }
+ ]
+}
diff --git a/src/entities/camposCustomizados/index.ts b/src/entities/camposCustomizados/index.ts
new file mode 100644
index 0000000..a80c00e
--- /dev/null
+++ b/src/entities/camposCustomizados/index.ts
@@ -0,0 +1,166 @@
+import { Entity } from '../@shared/entity'
+import {
+ IChangeSituationBody,
+ IChangeSituationParams
+} from './interfaces/change-situation.interface'
+import { ICreateBody, ICreateResponse } from './interfaces/create.interface'
+import { IDeleteParams } from './interfaces/delete.interface'
+import {
+ IFindByModuleParams,
+ IFindByModuleResponse
+} from './interfaces/find-by-module.interface'
+import { IFindParams, IFindResponse } from './interfaces/find.interface'
+import { IGetModuleResponse } from './interfaces/get-modules.interface'
+import { IGetTypeResponse } from './interfaces/get-types.interface'
+import {
+ IUpdateBody,
+ IUpdateParams,
+ IUpdateResponse
+} from './interfaces/update.interface'
+
+/**
+ * Entidade para interação com campos customizados.
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados
+ */
+export class CamposCustomizados extends Entity {
+ /**
+ * Remove um campo customizado.
+ *
+ * @param params Parâmetros da deleção.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados/delete_campos_customizados__idCampoCustomizado_
+ */
+ async delete(params: IDeleteParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'campos-customizados',
+ id: String(params.idCampoCustomizado)
+ })
+ }
+
+ /**
+ * Obtém módulos que possuem campos customizados.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados/get_campos_customizados_modulos
+ */
+ async getModules(): Promise {
+ return await this.repository.index({
+ endpoint: 'campos-customizados/modulos'
+ })
+ }
+
+ /**
+ * Obtém tipos de campos customizados.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados/get_campos_customizados_tipos
+ */
+ async getTypes(): Promise {
+ return await this.repository.index({
+ endpoint: 'campos-customizados/tipos'
+ })
+ }
+
+ /**
+ * Obtém campos customizados por módulo.
+ *
+ * @param {IFindByModuleParams} params Parâmetros da busca
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados/get_campos_customizados_modulos__idModulo_
+ */
+ async findByModule(
+ params: IFindByModuleParams
+ ): Promise {
+ return await this.repository.show({
+ endpoint: 'campos-customizados/modulos',
+ id: String(params.idModulo),
+ params: {
+ pagina: params.pagina,
+ limite: params.limite
+ }
+ })
+ }
+
+ /**
+ * Obtém um campo customizado.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados/get_campos_customizados__idCampoCustomizado_
+ */
+ async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'campos-customizados',
+ id: String(params.idCampoCustomizado)
+ })
+ }
+
+ /**
+ * Altera a situação de um campo customizado.
+ *
+ * @param {IChangeSituationParams & IChangeSituationBody} params Parâmetros da atualização.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados/patch_campos_customizados__idCampoCustomizado__situacoes
+ */
+ async changeSituation(
+ params: IChangeSituationParams & IChangeSituationBody
+ ): Promise {
+ return await this.repository.update({
+ endpoint: 'campos-customizados',
+ id: `${params.idCampoCustomizado}/situacoes`,
+ body: { situacao: params.situacao }
+ })
+ }
+
+ /**
+ * Cria um campo customizado.
+ *
+ * @param {ICreateBody} body O corpo da requisição.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados/post_campos_customizados
+ */
+ async create(body: ICreateBody): Promise {
+ return await this.repository.store({
+ endpoint: 'campos-customizados',
+ body
+ })
+ }
+
+ /**
+ * Altera um campo customizado.
+ *
+ * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Campos%20Customizados/put_campos_customizados__idCampoCustomizado_
+ */
+ async update(params: IUpdateParams & IUpdateBody): Promise {
+ const { idCampoCustomizado, ...body } = params
+
+ return await this.repository.update({
+ endpoint: 'campos-customizados',
+ id: String(idCampoCustomizado),
+ body
+ })
+ }
+}
diff --git a/src/entities/camposCustomizados/interfaces/change-situation.interface.ts b/src/entities/camposCustomizados/interfaces/change-situation.interface.ts
new file mode 100644
index 0000000..79501dc
--- /dev/null
+++ b/src/entities/camposCustomizados/interfaces/change-situation.interface.ts
@@ -0,0 +1,9 @@
+import ISituacao from '../../@shared/types/situacao.type'
+
+export interface IChangeSituationParams {
+ idCampoCustomizado: number
+}
+
+export interface IChangeSituationBody {
+ situacao: ISituacao
+}
diff --git a/src/entities/camposCustomizados/interfaces/create.interface.ts b/src/entities/camposCustomizados/interfaces/create.interface.ts
new file mode 100644
index 0000000..b6eaddf
--- /dev/null
+++ b/src/entities/camposCustomizados/interfaces/create.interface.ts
@@ -0,0 +1,27 @@
+import ISituacao from '../../@shared/types/situacao.type'
+
+export interface ICreateBody {
+ nome: string
+ situacao?: ISituacao
+ placeholder?: string
+ obrigatorio?: boolean
+ opcoes?: {
+ id: number
+ nome: string
+ }[]
+ tamanho?: {
+ minimo?: number
+ maximo?: number
+ }
+ agrupadores?: { id: number }[]
+ modulo: { id: number }
+ tipoCampo: { id: number }
+}
+
+export interface ICreateResponse {
+ data: {
+ id: number
+ idsVinculosAgrupadores: number[]
+ idsOpcoes: number[]
+ }
+}
diff --git a/src/entities/camposCustomizados/interfaces/delete.interface.ts b/src/entities/camposCustomizados/interfaces/delete.interface.ts
new file mode 100644
index 0000000..7d0b9ed
--- /dev/null
+++ b/src/entities/camposCustomizados/interfaces/delete.interface.ts
@@ -0,0 +1,3 @@
+export interface IDeleteParams {
+ idCampoCustomizado: number
+}
diff --git a/src/entities/camposCustomizados/interfaces/find-by-module.interface.ts b/src/entities/camposCustomizados/interfaces/find-by-module.interface.ts
new file mode 100644
index 0000000..78cb22d
--- /dev/null
+++ b/src/entities/camposCustomizados/interfaces/find-by-module.interface.ts
@@ -0,0 +1,15 @@
+import ISituacao from '../../@shared/types/situacao.type'
+
+export interface IFindByModuleParams {
+ idModulo: number
+ pagina?: number
+ limite?: number
+}
+
+export interface IFindByModuleResponse {
+ data: {
+ id: number
+ nome: string
+ situacao: ISituacao
+ }[]
+}
diff --git a/src/entities/camposCustomizados/interfaces/find.interface.ts b/src/entities/camposCustomizados/interfaces/find.interface.ts
new file mode 100644
index 0000000..3af7187
--- /dev/null
+++ b/src/entities/camposCustomizados/interfaces/find.interface.ts
@@ -0,0 +1,26 @@
+import ISituacao from '../../@shared/types/situacao.type'
+
+export interface IFindParams {
+ idCampoCustomizado: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ nome: string
+ situacao: ISituacao
+ placeholder: string
+ obrigatorio: boolean
+ opcoes: {
+ id: number
+ nome: string
+ }[]
+ tamanho: {
+ minimo: number
+ maximo: number
+ }
+ agrupadores: { id: number }[]
+ modulo: { id: number }
+ tipoCampo: { id: number }
+ }
+}
diff --git a/src/entities/camposCustomizados/interfaces/get-modules.interface.ts b/src/entities/camposCustomizados/interfaces/get-modules.interface.ts
new file mode 100644
index 0000000..f361b80
--- /dev/null
+++ b/src/entities/camposCustomizados/interfaces/get-modules.interface.ts
@@ -0,0 +1,13 @@
+export interface IGetModuleResponse {
+ data: {
+ id: number
+ nome: string
+ modulo: string
+ agrupador: string
+ permissoes: {
+ nome: string
+ modulo: string
+ autorizado: boolean
+ }[]
+ }[]
+}
diff --git a/src/entities/camposCustomizados/interfaces/get-types.interface.ts b/src/entities/camposCustomizados/interfaces/get-types.interface.ts
new file mode 100644
index 0000000..83cd2e2
--- /dev/null
+++ b/src/entities/camposCustomizados/interfaces/get-types.interface.ts
@@ -0,0 +1,7 @@
+export interface IGetTypeResponse {
+ data: {
+ id: number
+ nome: string
+ mascara: string
+ }[]
+}
diff --git a/src/entities/camposCustomizados/interfaces/update.interface.ts b/src/entities/camposCustomizados/interfaces/update.interface.ts
new file mode 100644
index 0000000..ae687fe
--- /dev/null
+++ b/src/entities/camposCustomizados/interfaces/update.interface.ts
@@ -0,0 +1,29 @@
+import ISituacao from '../../@shared/types/situacao.type'
+
+export interface IUpdateParams {
+ idCampoCustomizado: number
+}
+
+export interface IUpdateBody {
+ nome: string
+ situacao?: ISituacao
+ placeholder?: string
+ obrigatorio?: boolean
+ opcoes?: {
+ id: number
+ nome: string
+ }[]
+ tamanho?: {
+ minimo?: number
+ maximo?: number
+ }
+ agrupadores: { id: number }[]
+}
+
+export interface IUpdateResponse {
+ data: {
+ id: number
+ idsVinculosAgrupadores: number[]
+ idsOpcoes: number[]
+ }
+}
diff --git a/src/entities/categoriasLojas/__tests__/create-response.ts b/src/entities/categoriasLojas/__tests__/create-response.ts
new file mode 100644
index 0000000..37da4aa
--- /dev/null
+++ b/src/entities/categoriasLojas/__tests__/create-response.ts
@@ -0,0 +1,16 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const createRequestBody = {
+ loja: {
+ id: 12345678
+ },
+ descricao: 'Categoria de produto vinculado à loja',
+ codigo: '12345678',
+ categoriaProduto: {
+ id: 12345678
+ }
+}
diff --git a/src/entities/categoriasLojas/__tests__/delete-response.ts b/src/entities/categoriasLojas/__tests__/delete-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/categoriasLojas/__tests__/delete-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/categoriasLojas/__tests__/find-response.ts b/src/entities/categoriasLojas/__tests__/find-response.ts
new file mode 100644
index 0000000..0044a6f
--- /dev/null
+++ b/src/entities/categoriasLojas/__tests__/find-response.ts
@@ -0,0 +1,13 @@
+export default {
+ data: {
+ id: 12345678,
+ loja: {
+ id: 12345678
+ },
+ descricao: 'Categoria de produto vinculado à loja',
+ codigo: '12345678',
+ categoriaProduto: {
+ id: 12345678
+ }
+ }
+}
diff --git a/src/entities/categoriasLojas/__tests__/get-response.ts b/src/entities/categoriasLojas/__tests__/get-response.ts
new file mode 100644
index 0000000..0eae565
--- /dev/null
+++ b/src/entities/categoriasLojas/__tests__/get-response.ts
@@ -0,0 +1,15 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ loja: {
+ id: 12345678
+ },
+ descricao: 'Categoria de produto vinculado à loja',
+ codigo: '12345678',
+ categoriaProduto: {
+ id: 12345678
+ }
+ }
+ ]
+}
diff --git a/src/entities/categoriasLojas/__tests__/index.spec.ts b/src/entities/categoriasLojas/__tests__/index.spec.ts
new file mode 100644
index 0000000..eabad54
--- /dev/null
+++ b/src/entities/categoriasLojas/__tests__/index.spec.ts
@@ -0,0 +1,102 @@
+import { Chance } from 'chance'
+import { CategoriasLojas } from '../'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import createResponse, { createRequestBody } from './create-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('Categorias - Lojas entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: CategoriasLojas
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new CategoriasLojas(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should delete successfully', async () => {
+ const idCategoriaLoja = chance.natural()
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteResponse)
+
+ const response = await entity.delete({ idCategoriaLoja })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/lojas',
+ id: String(idCategoriaLoja)
+ })
+ expect(response).toBe(deleteResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idCategoriaLoja = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idCategoriaLoja })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/lojas',
+ id: String(idCategoriaLoja)
+ })
+ expect(response).toBe(findResponse)
+ })
+
+ it('should get successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getResponse)
+
+ const response = await entity.get()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/lojas',
+ params: {
+ idCategoriaProduto: undefined,
+ idCategoriaProdutoPai: undefined,
+ idLoja: undefined,
+ limite: undefined,
+ pagina: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should create successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(createResponse)
+
+ const response = await entity.create(createRequestBody)
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/lojas',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idCategoriaLoja = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idCategoriaLoja,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/lojas',
+ id: String(idCategoriaLoja),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/categoriasLojas/__tests__/update-response.ts b/src/entities/categoriasLojas/__tests__/update-response.ts
new file mode 100644
index 0000000..1435b7d
--- /dev/null
+++ b/src/entities/categoriasLojas/__tests__/update-response.ts
@@ -0,0 +1,12 @@
+export default null
+
+export const updateRequestBody = {
+ loja: {
+ id: 12345678
+ },
+ descricao: 'Categoria de produto vinculado à loja',
+ codigo: '12345678',
+ categoriaProduto: {
+ id: 12345678
+ }
+}
diff --git a/src/entities/categoriasLojas/index.ts b/src/entities/categoriasLojas/index.ts
new file mode 100644
index 0000000..35f57d3
--- /dev/null
+++ b/src/entities/categoriasLojas/index.ts
@@ -0,0 +1,107 @@
+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 { IGetParams, IGetResponse } from './interfaces/get.interface'
+import { IUpdateBody, IUpdateParams } from './interfaces/update.interface'
+
+/**
+ * Entidade para interação com Categorias - Lojas.
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Lojas
+ */
+export class CategoriasLojas extends Entity {
+ /**
+ * Remove o vínculo de uma categoria da loja com a de produto.
+ *
+ * @param {IDeleteParams} params Parâmetros da remoção.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Lojas/delete_categorias_lojas__idCategoriaLoja_
+ */
+ public async delete(params: IDeleteParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'categorias/lojas',
+ id: String(params.idCategoriaLoja)
+ })
+ }
+
+ /**
+ * Obtém categorias de lojas virtuais vinculadas a de produtos.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Lojas/get_categorias_lojas
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'categorias/lojas',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite,
+ idLoja: params?.idLoja,
+ idCategoriaProduto: params?.idCategoriaProduto,
+ idCategoriaProdutoPai: params?.idCategoriaProdutoPai
+ }
+ })
+ }
+
+ /**
+ * Obtém uma categoria da loja vinculada a de produto.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Lojas/get_categorias_lojas__idCategoriaLoja_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'categorias/lojas',
+ id: String(params.idCategoriaLoja)
+ })
+ }
+
+ /**
+ * Cria o vínculo de uma categoria da loja com a de produto.
+ *
+ * @param {ICreateBody} body O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Lojas/post_categorias_lojas
+ */
+ public async create(body: ICreateBody): Promise {
+ return await this.repository.store({
+ endpoint: 'categorias/lojas',
+ body
+ })
+ }
+
+ /**
+ * Altera o vínculo de uma categoria da loja com a de produto.
+ *
+ * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização.
+ *
+ * @return {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Lojas/put_categorias_lojas__idCategoriaLoja_
+ */
+ public async update(params: IUpdateParams & IUpdateBody): Promise {
+ const { idCategoriaLoja, ...body } = params
+
+ return await this.repository.replace({
+ endpoint: 'categorias/lojas',
+ id: String(idCategoriaLoja),
+ body
+ })
+ }
+}
diff --git a/src/entities/categoriasLojas/interfaces/create.interface.ts b/src/entities/categoriasLojas/interfaces/create.interface.ts
new file mode 100644
index 0000000..970b4cb
--- /dev/null
+++ b/src/entities/categoriasLojas/interfaces/create.interface.ts
@@ -0,0 +1,12 @@
+export interface ICreateBody {
+ loja: { id: number }
+ descricao: string
+ codigo: string
+ categoriaProduto: { id: number }
+}
+
+export interface ICreateResponse {
+ data: {
+ id: number
+ }
+}
diff --git a/src/entities/categoriasLojas/interfaces/delete.interface.ts b/src/entities/categoriasLojas/interfaces/delete.interface.ts
new file mode 100644
index 0000000..4781798
--- /dev/null
+++ b/src/entities/categoriasLojas/interfaces/delete.interface.ts
@@ -0,0 +1,3 @@
+export interface IDeleteParams {
+ idCategoriaLoja: number
+}
diff --git a/src/entities/categoriasLojas/interfaces/find.interface.ts b/src/entities/categoriasLojas/interfaces/find.interface.ts
new file mode 100644
index 0000000..e063044
--- /dev/null
+++ b/src/entities/categoriasLojas/interfaces/find.interface.ts
@@ -0,0 +1,13 @@
+export interface IFindParams {
+ idCategoriaLoja: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ loja: { id: number }
+ descricao: string
+ codigo: string
+ categoriaProduto: { id: number }
+ }
+}
diff --git a/src/entities/categoriasLojas/interfaces/get.interface.ts b/src/entities/categoriasLojas/interfaces/get.interface.ts
new file mode 100644
index 0000000..5d8b1c7
--- /dev/null
+++ b/src/entities/categoriasLojas/interfaces/get.interface.ts
@@ -0,0 +1,17 @@
+export interface IGetParams {
+ pagina?: number
+ limite?: number
+ idLoja?: number
+ idCategoriaProduto?: number
+ idCategoriaProdutoPai?: number
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ loja: { id: number }
+ descricao: string
+ codigo: string
+ categoriaProduto: { id: number }
+ }[]
+}
diff --git a/src/entities/categoriasLojas/interfaces/update.interface.ts b/src/entities/categoriasLojas/interfaces/update.interface.ts
new file mode 100644
index 0000000..e5262d5
--- /dev/null
+++ b/src/entities/categoriasLojas/interfaces/update.interface.ts
@@ -0,0 +1,10 @@
+export interface IUpdateParams {
+ idCategoriaLoja: number
+}
+
+export interface IUpdateBody {
+ loja: { id: number }
+ descricao: string
+ codigo: string
+ categoriaProduto: { id: number }
+}
diff --git a/src/entities/categoriasProdutos/__tests__/create-response.ts b/src/entities/categoriasProdutos/__tests__/create-response.ts
new file mode 100644
index 0000000..448b5e3
--- /dev/null
+++ b/src/entities/categoriasProdutos/__tests__/create-response.ts
@@ -0,0 +1,12 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const createRequestBody = {
+ descricao: 'Eletrônicos',
+ categoriaPai: {
+ id: 12345678
+ }
+}
diff --git a/src/entities/categoriasProdutos/__tests__/delete-response.ts b/src/entities/categoriasProdutos/__tests__/delete-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/categoriasProdutos/__tests__/delete-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/categoriasProdutos/__tests__/find-response.ts b/src/entities/categoriasProdutos/__tests__/find-response.ts
new file mode 100644
index 0000000..b738a69
--- /dev/null
+++ b/src/entities/categoriasProdutos/__tests__/find-response.ts
@@ -0,0 +1,9 @@
+export default {
+ data: {
+ id: 12345678,
+ descricao: 'Eletrônicos',
+ categoriaPai: {
+ id: 12345678
+ }
+ }
+}
diff --git a/src/entities/categoriasProdutos/__tests__/get-response.ts b/src/entities/categoriasProdutos/__tests__/get-response.ts
new file mode 100644
index 0000000..6a36edf
--- /dev/null
+++ b/src/entities/categoriasProdutos/__tests__/get-response.ts
@@ -0,0 +1,11 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ descricao: 'Eletrônicos',
+ categoriaPai: {
+ id: 12345678
+ }
+ }
+ ]
+}
diff --git a/src/entities/categoriasProdutos/__tests__/index.spec.ts b/src/entities/categoriasProdutos/__tests__/index.spec.ts
new file mode 100644
index 0000000..2ea77c0
--- /dev/null
+++ b/src/entities/categoriasProdutos/__tests__/index.spec.ts
@@ -0,0 +1,99 @@
+import { Chance } from 'chance'
+import { CategoriasProdutos } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import createResponse, { createRequestBody } from './create-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('Categorias - Produtos entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: CategoriasProdutos
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new CategoriasProdutos(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should delete successfully', async () => {
+ const idCategoriaProduto = chance.natural()
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteResponse)
+
+ const response = await entity.delete({ idCategoriaProduto })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/produtos',
+ id: String(idCategoriaProduto)
+ })
+ expect(response).toBe(deleteResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idCategoriaProduto = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idCategoriaProduto })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/produtos',
+ id: String(idCategoriaProduto)
+ })
+ expect(response).toBe(findResponse)
+ })
+
+ it('should get successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getResponse)
+
+ const response = await entity.get()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/produtos',
+ params: {
+ limite: undefined,
+ pagina: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should create successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(createResponse)
+
+ const response = await entity.create(createRequestBody)
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/produtos',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idCategoriaProduto = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idCategoriaProduto,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/produtos',
+ id: String(idCategoriaProduto),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/categoriasProdutos/__tests__/update-response.ts b/src/entities/categoriasProdutos/__tests__/update-response.ts
new file mode 100644
index 0000000..06e1838
--- /dev/null
+++ b/src/entities/categoriasProdutos/__tests__/update-response.ts
@@ -0,0 +1,5 @@
+export default null
+
+export const updateRequestBody = {
+ descricao: 'Eletrônicos'
+}
diff --git a/src/entities/categoriasProdutos/index.ts b/src/entities/categoriasProdutos/index.ts
new file mode 100644
index 0000000..4225818
--- /dev/null
+++ b/src/entities/categoriasProdutos/index.ts
@@ -0,0 +1,104 @@
+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 { IGetParams, IGetResponse } from './interfaces/get.interface'
+import { IUpdateBody, IUpdateParams } from './interfaces/update.interface'
+
+/**
+ * Entidade para interação com Categorias - Produtos.
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Produtos
+ */
+export class CategoriasProdutos extends Entity {
+ /**
+ * Remove uma categoria de produto.
+ *
+ * @param {IDeleteParams} params Parâmetros da remoção.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Produtos/delete_categorias_produtos__idCategoriaProduto_
+ */
+ public async delete(params: IDeleteParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'categorias/produtos',
+ id: String(params.idCategoriaProduto)
+ })
+ }
+
+ /**
+ * Obtém categorias de produtos.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Produtos/get_categorias_produtos
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'categorias/produtos',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite
+ }
+ })
+ }
+
+ /**
+ * Obtém uma categoria de produto.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Produtos/get_categorias_produtos__idCategoriaProduto_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'categorias/produtos',
+ id: String(params.idCategoriaProduto)
+ })
+ }
+
+ /**
+ * Cria uma categoria de produto.
+ *
+ * @param {ICreateBody} body O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Produtos/post_categorias_produtos
+ */
+ public async create(body: ICreateBody): Promise {
+ return await this.repository.store({
+ endpoint: 'categorias/produtos',
+ body
+ })
+ }
+
+ /**
+ * Altera uma categoria de produto.
+ *
+ * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização.
+ *
+ * @return {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Produtos/put_categorias_produtos__idCategoriaProduto_
+ */
+ public async update(params: IUpdateParams & IUpdateBody): Promise {
+ const { idCategoriaProduto, ...body } = params
+
+ return await this.repository.replace({
+ endpoint: 'categorias/produtos',
+ id: String(idCategoriaProduto),
+ body
+ })
+ }
+}
diff --git a/src/entities/categoriasProdutos/interfaces/create.interface.ts b/src/entities/categoriasProdutos/interfaces/create.interface.ts
new file mode 100644
index 0000000..10a2189
--- /dev/null
+++ b/src/entities/categoriasProdutos/interfaces/create.interface.ts
@@ -0,0 +1,12 @@
+export interface ICreateBody {
+ descricao: string
+ categoriaPai?: {
+ id: number
+ }
+}
+
+export interface ICreateResponse {
+ data: {
+ id: number
+ }
+}
diff --git a/src/entities/categoriasProdutos/interfaces/delete.interface.ts b/src/entities/categoriasProdutos/interfaces/delete.interface.ts
new file mode 100644
index 0000000..46de23e
--- /dev/null
+++ b/src/entities/categoriasProdutos/interfaces/delete.interface.ts
@@ -0,0 +1,3 @@
+export interface IDeleteParams {
+ idCategoriaProduto: number
+}
diff --git a/src/entities/categoriasProdutos/interfaces/find.interface.ts b/src/entities/categoriasProdutos/interfaces/find.interface.ts
new file mode 100644
index 0000000..c6a2e07
--- /dev/null
+++ b/src/entities/categoriasProdutos/interfaces/find.interface.ts
@@ -0,0 +1,13 @@
+export interface IFindParams {
+ idCategoriaProduto: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ descricao: string
+ categoriaPai: {
+ id: number
+ }
+ }
+}
diff --git a/src/entities/categoriasProdutos/interfaces/get.interface.ts b/src/entities/categoriasProdutos/interfaces/get.interface.ts
new file mode 100644
index 0000000..6e55b2e
--- /dev/null
+++ b/src/entities/categoriasProdutos/interfaces/get.interface.ts
@@ -0,0 +1,14 @@
+export interface IGetParams {
+ pagina?: number
+ limite?: number
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ descricao: string
+ categoriaPai: {
+ id: number
+ }
+ }[]
+}
diff --git a/src/entities/categoriasProdutos/interfaces/update.interface.ts b/src/entities/categoriasProdutos/interfaces/update.interface.ts
new file mode 100644
index 0000000..059189c
--- /dev/null
+++ b/src/entities/categoriasProdutos/interfaces/update.interface.ts
@@ -0,0 +1,7 @@
+export interface IUpdateParams {
+ idCategoriaProduto: number
+}
+
+export interface IUpdateBody {
+ descricao: string
+}
diff --git a/src/entities/categoriasReceitasDespesas/__tests__/find-response.ts b/src/entities/categoriasReceitasDespesas/__tests__/find-response.ts
new file mode 100644
index 0000000..9095417
--- /dev/null
+++ b/src/entities/categoriasReceitasDespesas/__tests__/find-response.ts
@@ -0,0 +1,9 @@
+export default {
+ data: {
+ id: 12345678,
+ idCategoriaPai: 0,
+ descricao: 'Vendas de mercadorias',
+ tipo: 1,
+ situacao: 1
+ }
+}
diff --git a/src/entities/categoriasReceitasDespesas/__tests__/get-response.ts b/src/entities/categoriasReceitasDespesas/__tests__/get-response.ts
new file mode 100644
index 0000000..86a67ec
--- /dev/null
+++ b/src/entities/categoriasReceitasDespesas/__tests__/get-response.ts
@@ -0,0 +1,10 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ idCategoriaPai: 0,
+ descricao: 'Vendas de mercadorias',
+ tipo: 1
+ }
+ ]
+}
diff --git a/src/entities/categoriasReceitasDespesas/__tests__/index.spec.ts b/src/entities/categoriasReceitasDespesas/__tests__/index.spec.ts
new file mode 100644
index 0000000..1a3bfb9
--- /dev/null
+++ b/src/entities/categoriasReceitasDespesas/__tests__/index.spec.ts
@@ -0,0 +1,52 @@
+import { Chance } from 'chance'
+import { CategoriasReceitasDespesas } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import findResponse from './find-response'
+import getResponse from './get-response'
+const chance = Chance()
+
+describe('Categorias - Receitas e Despesas entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: CategoriasReceitasDespesas
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new CategoriasReceitasDespesas(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idCategoria = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idCategoria })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/receitas-despesas',
+ id: String(idCategoria)
+ })
+ expect(response).toBe(findResponse)
+ })
+
+ it('should get successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getResponse)
+
+ const response = await entity.get()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'categorias/receitas-despesas',
+ params: {
+ limite: undefined,
+ pagina: undefined,
+ situacao: undefined,
+ tipo: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+})
diff --git a/src/entities/categoriasReceitasDespesas/index.ts b/src/entities/categoriasReceitasDespesas/index.ts
new file mode 100644
index 0000000..b823e64
--- /dev/null
+++ b/src/entities/categoriasReceitasDespesas/index.ts
@@ -0,0 +1,49 @@
+import { Entity } from '../@shared/entity'
+import { IFindParams, IFindResponse } from './interfaces/find.interface'
+import { IGetParams, IGetResponse } from './interfaces/get.interface'
+
+/**
+ * Entidade para interação com Categorias - Receitas e Despesas.
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Receitas%20e%20Despesas
+ */
+export class CategoriasReceitasDespesas extends Entity {
+ /**
+ * Obtém categorias de receitas e despesas.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Receitas%20e%20Despesas/get_categorias_receitas_despesas
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'categorias/receitas-despesas',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite,
+ tipo: params?.tipo,
+ situacao: params?.situacao
+ }
+ })
+ }
+
+ /**
+ * Obtém uma categoria de receita e despesa.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Categorias%20-%20Receitas%20e%20Despesas/get_categorias_receitas_despesas__idCategoria_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'categorias/receitas-despesas',
+ id: String(params.idCategoria)
+ })
+ }
+}
diff --git a/src/entities/categoriasReceitasDespesas/interfaces/find.interface.ts b/src/entities/categoriasReceitasDespesas/interfaces/find.interface.ts
new file mode 100644
index 0000000..8b1447f
--- /dev/null
+++ b/src/entities/categoriasReceitasDespesas/interfaces/find.interface.ts
@@ -0,0 +1,16 @@
+import { ISituacao } from '../types/situacao.type'
+import { ITipo } from '../types/tipo.type'
+
+export interface IFindParams {
+ idCategoria: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ idCategoriaPai: number
+ descricao: string
+ tipo: ITipo
+ situacao: ISituacao
+ }
+}
diff --git a/src/entities/categoriasReceitasDespesas/interfaces/get.interface.ts b/src/entities/categoriasReceitasDespesas/interfaces/get.interface.ts
new file mode 100644
index 0000000..ba4d874
--- /dev/null
+++ b/src/entities/categoriasReceitasDespesas/interfaces/get.interface.ts
@@ -0,0 +1,18 @@
+import { ISituacao } from '../types/situacao.type'
+import { ITipo } from '../types/tipo.type'
+
+export interface IGetParams {
+ pagina?: number
+ limite?: number
+ tipo?: ITipo
+ situacao?: ISituacao
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ idCategoriaPai: number
+ descricao: string
+ tipo: ITipo
+ }[]
+}
diff --git a/src/entities/categoriasReceitasDespesas/types/situacao.type.ts b/src/entities/categoriasReceitasDespesas/types/situacao.type.ts
new file mode 100644
index 0000000..198ccc6
--- /dev/null
+++ b/src/entities/categoriasReceitasDespesas/types/situacao.type.ts
@@ -0,0 +1,8 @@
+/**
+ * Tipagem representativa da situação da categoria.
+ *
+ * `0`: Ativas e Inativas (padrão)
+ * `1`: Ativas
+ * `2`: Inativas
+ */
+export type ISituacao = 0 | 1 | 2
diff --git a/src/entities/categoriasReceitasDespesas/types/tipo.type.ts b/src/entities/categoriasReceitasDespesas/types/tipo.type.ts
new file mode 100644
index 0000000..e05524d
--- /dev/null
+++ b/src/entities/categoriasReceitasDespesas/types/tipo.type.ts
@@ -0,0 +1,9 @@
+/**
+ * Tipagem representativa do tipo da categoria.
+ *
+ * `0`: Todas (padrão)
+ * `1`: Despesa
+ * `2`: Receita
+ * `3`: Receita e despesa
+ */
+export type ITipo = 0 | 1 | 2 | 3
diff --git a/src/entities/categories.ts b/src/entities/categories.ts
deleted file mode 100644
index 2744276..0000000
--- a/src/entities/categories.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import All from '../core/functions/all'
-import Find from '../core/functions/find'
-import FindBy from '../core/functions/findBy'
-import Create from '../core/functions/create'
-import Update from '../core/functions/update'
-
-export interface ICategory {
- descricao: string
- idCategoriaPai?: number
-}
-
-export type ICategoryFilters = Record
-
-export type ICategoryInfos = Record
-
-export interface ICategoryResponse {
- id: number
- descricao: string
- idCategoriaPai: number
-}
-
-export default function Categories (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'categoria',
- pluralName: 'categorias'
- }
-
- return Object.assign(config, {
- all: new All().all,
- find: new Find().find,
- findBy: new FindBy()
- .findBy,
- create: new Create().create,
- update: new Update().update
- })
-}
diff --git a/src/entities/commercialProposals.ts b/src/entities/commercialProposals.ts
deleted file mode 100644
index 483f210..0000000
--- a/src/entities/commercialProposals.ts
+++ /dev/null
@@ -1,193 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import All from '../core/functions/all'
-import Find from '../core/functions/find'
-import FindBy from '../core/functions/findBy'
-import Create from '../core/functions/create'
-import Update from '../core/functions/update'
-
-export interface ICommercialProposal {
- data?: string
- dataProximoContato?: string
- contatoAc?: string
- loja?: number
- numero?: number
- vendedor?: string
- desconto?: string
- outrasDespesas?: number
- validade?: number
- prazoEntrega?: string
- garantia?: number
- observacao?: string
- obsInterna?: string
- assinaturaSaudacao?: string
- assinaturaResponsavel?: string
- cliente: {
- id?: number | string
- nome: string
- tipoPessoa?: 'F' | 'J' | 'E'
- cpfCnpj?: string
- ie?: string
- rg?: string
- contribuinte?: '1' | '2' | '3'
- endereco?: string
- numero?: string
- complemento?: string
- bairro?: string
- cep?: string
- cidade?: string
- uf?: string
- fone?: string
- celular?: string
- email?: string
- }
- itens: {
- item: {
- codigo?: string
- descricao?: string
- un?: string
- qtde: number
- valorUnidade: number | string
- }
- }[]
- transporte?: {
- transportadora?: string
- tipoFrete?: 'R' | 'D' | 'T' | '3' | '4' | '5'
- frete?: number
- }
- parcelas?: {
- parcela: {
- nrDias: number
- valor: number
- obs?: string
- formaPagamento: {
- id: number
- }
- }
- }[]
-}
-
-export interface ICommercialProposalUpdateContent {
- situacao:
- | 'Descrição'
- | 'Pendente'
- | 'Aguardando'
- | 'Não aprovado'
- | 'Aprovado'
- | 'Concluído'
- | 'Rascunho'
-}
-
-export interface ICommercialProposalFilters {
- data?: string
- situacao?:
- | 'Descrição'
- | 'Pendente'
- | 'Aguardando'
- | 'Não aprovado'
- | 'Aprovado'
- | 'Concluído'
- | 'Rascunho'
- idContato?: number
-}
-
-export type ICommercialProposalInfos = Record
-
-export interface ICommercialProposalCreateResponse {
- id: number
- numero: number
-}
-
-export interface ICommercialProposalUpdateResponse {
- numero: number
- mensagem: number
-}
-
-export interface ICommercialProposalResponse {
- desconto: string
- obsInterna?: string
- data: string
- dataProximoContato: string
- numeroProposta: string
- vendedor?: string
- valorFrete: string
- subtotal: string
- totalOrcamento: string
- situacao:
- | 'Descrição'
- | 'Pendente'
- | 'Aguardando'
- | 'Não aprovado'
- | 'Aprovado'
- | 'Concluído'
- | 'Rascunho'
- loja: string
- aosCuidadosDe?: string
- garantia: string
- validadeDaProposta: string
- observacao?: string
- prazoEntrega?: string
- assinaturaSaudacao?: string
- assinaturaResponsavel?: string
- cliente: {
- idContato: string
- nome: string
- cpfCnpj?: string
- ie: string | 'ISENTO'
- rg?: string
- endereco?: string
- numero?: string
- complemento?: string
- cidade?: string
- cep?: string
- uf?: string
- email?: string
- celular?: string
- fone?: string
- }
- itens: {
- item: {
- codigo: string
- descricao: string
- quantidade: string
- valorUnidade: string
- precoLista: string
- descontoItem: string
- un: string
- }
- }[]
- transporte: {
- transportadora?: string
- tipoFrete?: string
- qtdVolumes?: string
- pesoBruto?: string
- }
-}
-
-export default function CommercialProposals (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'propostacomercial',
- pluralName: 'propostascomerciais'
- }
-
- return Object.assign(config, {
- all: new All<
- ICommercialProposalResponse,
- ICommercialProposalFilters,
- ICommercialProposalInfos
- >().all,
- find: new Find()
- .find,
- findBy: new FindBy<
- ICommercialProposalResponse,
- ICommercialProposalFilters,
- ICommercialProposalInfos
- >().findBy,
- create: new Create()
- .create,
- update: new Update()
- .update
- })
-}
diff --git a/src/entities/contacts.ts b/src/entities/contacts.ts
deleted file mode 100644
index 28056b2..0000000
--- a/src/entities/contacts.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import All from '../core/functions/all'
-import Find from '../core/functions/find'
-import FindBy from '../core/functions/findBy'
-import Create from '../core/functions/create'
-import Update from '../core/functions/update'
-
-export interface IContact {
- nome: string
- fantasia?: string
- tipoPessoa: 'F' | 'J' | 'E'
- contribuinte: '1' | '2' | '9'
- cpf_cnpj: string
- ie_rg?: string
- endereco?: string
- numero?: string
- complemento?: string
- bairro?: string
- cep?: string
- cidade?: string
- uf?: string
- fone?: string
- celular?: string
- email?: string
- emailNfe?: string
- informacaoContato?: string
- limiteCredito?: number
- paisOrigem?: string
- codigo?: string
- site?: string
- obs?: string
- tipos_contatos?: {
- tipo_contato: {
- descricao?: string
- }
- }[]
-}
-
-export interface IContactFilters {
- dataInclusao?: string
- dataAlteracao?: string
- tipoPessoa?: 'F' | 'J' | 'E'
-}
-
-export interface IContactInfos {
- identificador?: '1' | '2'
-}
-
-export interface IContactResponse {
- id: string
- codigo?: string
- nome: string
- fantasia?: string
- tipo: 'F' | 'J' | 'E'
- cpf: string
- cnpj: string
- ie_rg?: string
- endereco?: string
- numero?: string
- bairro?: string
- cep?: string
- cidade?: string
- complemento?: string
- uf?: string
- fone?: string
- email?: string
- situacao: string
- contribuinte: '1' | '2' | '9'
- site?: string
- celular?: string
- dataAlteracao: string
- dataInclusao: string
- sexo?: string
- clienteDesde: string
- limiteCredito: string
- dataNascimento?: string
- informacoesContato?: string
-}
-
-export interface IContactCreateResponse {
- id: number
- nome: string
- cpf_cnpj: string
-}
-
-export interface IContactUpdateResponse {
- id: string
- nome: string
- cpf_cnpj: string
-}
-
-export default function Contacts (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'contato',
- pluralName: 'contatos'
- }
-
- return Object.assign(config, {
- all: new All().all,
- find: new Find().find,
- findBy: new FindBy()
- .findBy,
- create: new Create().create,
- update: new Update().update
- })
-}
diff --git a/src/entities/contasContabeis/__tests__/find-response.ts b/src/entities/contasContabeis/__tests__/find-response.ts
new file mode 100644
index 0000000..d6c68a9
--- /dev/null
+++ b/src/entities/contasContabeis/__tests__/find-response.ts
@@ -0,0 +1,6 @@
+export default {
+ data: {
+ id: 12345678,
+ descricao: 'Contas a pagar'
+ }
+}
diff --git a/src/entities/contasContabeis/__tests__/get-response.ts b/src/entities/contasContabeis/__tests__/get-response.ts
new file mode 100644
index 0000000..5401570
--- /dev/null
+++ b/src/entities/contasContabeis/__tests__/get-response.ts
@@ -0,0 +1,8 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ descricao: 'Contas a pagar'
+ }
+ ]
+}
diff --git a/src/entities/contasContabeis/__tests__/index.spec.ts b/src/entities/contasContabeis/__tests__/index.spec.ts
new file mode 100644
index 0000000..5ed0af7
--- /dev/null
+++ b/src/entities/contasContabeis/__tests__/index.spec.ts
@@ -0,0 +1,51 @@
+import { Chance } from 'chance'
+import { ContasContabeis } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import findResponse from './find-response'
+import getResponse from './get-response'
+
+const chance = Chance()
+
+describe('Contas contábeis entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: ContasContabeis
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new ContasContabeis(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should get successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getResponse)
+
+ const response = await entity.get()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas-contabeis',
+ params: {
+ limite: undefined,
+ pagina: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idContaContabil = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idContaContabil })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas-contabeis',
+ id: String(idContaContabil)
+ })
+ expect(response).toBe(findResponse)
+ })
+})
diff --git a/src/entities/contasContabeis/index.ts b/src/entities/contasContabeis/index.ts
new file mode 100644
index 0000000..fb6018e
--- /dev/null
+++ b/src/entities/contasContabeis/index.ts
@@ -0,0 +1,47 @@
+import { Entity } from '../@shared/entity'
+import { IFindParams, IFindResponse } from './interfaces/find.interface'
+import { IGetParams, IGetResponse } from './interfaces/get.interface'
+
+/**
+ * Entidade para interação com Contas Contábeis.
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20Cont%C3%A1beis
+ */
+export class ContasContabeis extends Entity {
+ /**
+ * Obtém contas contábeis.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20Cont%C3%A1beis/get_contas_contabeis
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'contas-contabeis',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite
+ }
+ })
+ }
+
+ /**
+ * Obtém uma conta contábil.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20Cont%C3%A1beis/get_contas_contabeis__idContaContabil_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'contas-contabeis',
+ id: String(params.idContaContabil)
+ })
+ }
+}
diff --git a/src/entities/contasContabeis/interfaces/find.interface.ts b/src/entities/contasContabeis/interfaces/find.interface.ts
new file mode 100644
index 0000000..28481f4
--- /dev/null
+++ b/src/entities/contasContabeis/interfaces/find.interface.ts
@@ -0,0 +1,10 @@
+export interface IFindParams {
+ idContaContabil: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ descricao: string
+ }
+}
diff --git a/src/entities/contasContabeis/interfaces/get.interface.ts b/src/entities/contasContabeis/interfaces/get.interface.ts
new file mode 100644
index 0000000..524ec4a
--- /dev/null
+++ b/src/entities/contasContabeis/interfaces/get.interface.ts
@@ -0,0 +1,11 @@
+export interface IGetParams {
+ pagina?: number
+ limite?: number
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ descricao: string
+ }[]
+}
diff --git a/src/entities/contasPagar/__tests__/create-response.ts b/src/entities/contasPagar/__tests__/create-response.ts
new file mode 100644
index 0000000..9fedd70
--- /dev/null
+++ b/src/entities/contasPagar/__tests__/create-response.ts
@@ -0,0 +1,18 @@
+export default {
+ id: 12345678
+}
+
+export const createRequestBody = {
+ vencimento: '2023-01-12',
+ valor: 1500.75,
+ contato: { id: 12345678 },
+ formaPagamento: { id: 12345678 },
+ saldo: 100.75,
+ dataEmissao: '2023-01-12',
+ numeroDocumento: '',
+ competencia: '2023-01-12',
+ historico: '',
+ portador: { id: 12345678 },
+ categoria: { id: 12345678 },
+ ocorrencia: { tipo: 1 as const }
+}
diff --git a/src/entities/contasPagar/__tests__/delete-response.ts b/src/entities/contasPagar/__tests__/delete-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/contasPagar/__tests__/delete-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/contasPagar/__tests__/download-response.ts b/src/entities/contasPagar/__tests__/download-response.ts
new file mode 100644
index 0000000..edc870c
--- /dev/null
+++ b/src/entities/contasPagar/__tests__/download-response.ts
@@ -0,0 +1,21 @@
+export default {
+ bordero: {
+ id: 12345678
+ }
+}
+
+export const downloadRequestBody = {
+ data: '2023-01-12',
+ usarDataVencimento: false,
+ portador: {
+ id: 12345678
+ },
+ categoria: {
+ id: 12345678
+ },
+ historico: '',
+ juros: 10.5,
+ desconto: 10.5,
+ acrescimo: 10.5,
+ valorRecebido: 100.5
+}
diff --git a/src/entities/contasPagar/__tests__/find-response.ts b/src/entities/contasPagar/__tests__/find-response.ts
new file mode 100644
index 0000000..c72c4af
--- /dev/null
+++ b/src/entities/contasPagar/__tests__/find-response.ts
@@ -0,0 +1,28 @@
+export default {
+ data: {
+ id: 12345678,
+ situacao: 1,
+ vencimento: '2023-01-12',
+ valor: 1500.75,
+ contato: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ },
+ saldo: 100.75,
+ dataEmissao: '2023-01-12',
+ vencimentoOriginal: '2023-01-12',
+ numeroDocumento: '',
+ competencia: '2023-01-12',
+ historico: '',
+ numeroBanco: '',
+ portador: {
+ id: 12345678
+ },
+ categoria: {
+ id: 12345678
+ },
+ borderos: [0]
+ }
+}
diff --git a/src/entities/contasPagar/__tests__/get-response.ts b/src/entities/contasPagar/__tests__/get-response.ts
new file mode 100644
index 0000000..5435477
--- /dev/null
+++ b/src/entities/contasPagar/__tests__/get-response.ts
@@ -0,0 +1,16 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ situacao: 1,
+ vencimento: '2023-01-12',
+ valor: 1500.75,
+ contato: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ }
+ }
+ ]
+}
diff --git a/src/entities/contasPagar/__tests__/index.spec.ts b/src/entities/contasPagar/__tests__/index.spec.ts
new file mode 100644
index 0000000..9de85c0
--- /dev/null
+++ b/src/entities/contasPagar/__tests__/index.spec.ts
@@ -0,0 +1,125 @@
+import { Chance } from 'chance'
+import { ContasPagar } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import createResponse, { createRequestBody } from './create-response'
+import deleteResponse from './delete-response'
+import downloadResponse, { downloadRequestBody } from './download-response'
+import findResponse from './find-response'
+import getResponse from './get-response'
+import updateResponse, { updateRequestBody } from './update-response'
+
+const chance = Chance()
+
+describe('Contas a pagar entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: ContasPagar
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new ContasPagar(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should delete successfully', async () => {
+ const idContaPagar = chance.natural()
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteResponse)
+
+ const response = await entity.delete({ idContaPagar })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/pagar',
+ id: String(idContaPagar)
+ })
+ expect(response).toBe(deleteResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idContaPagar = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idContaPagar })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/pagar',
+ id: String(idContaPagar)
+ })
+ expect(response).toBe(findResponse)
+ })
+
+ it('should get successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getResponse)
+
+ const response = await entity.get()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/pagar',
+ params: {
+ limite: undefined,
+ pagina: undefined,
+ dataEmissaoInicial: undefined,
+ dataEmissaoFinal: undefined,
+ dataVencimentoInicial: undefined,
+ dataVencimentoFinal: undefined,
+ dataPagamentoInicial: undefined,
+ dataPagamentoFinal: undefined,
+ situacao: undefined,
+ idContato: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should create successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(createResponse)
+
+ const response = await entity.create(createRequestBody)
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/pagar',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should download successfully', async () => {
+ const idContaPagar = chance.natural()
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(downloadResponse)
+
+ const response = await entity.download({
+ idContaPagar,
+ ...downloadRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: `contas/pagar/${idContaPagar}/baixar`,
+ body: downloadRequestBody
+ })
+ expect(response).toBe(downloadResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idContaPagar = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idContaPagar,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/pagar',
+ id: String(idContaPagar),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/contasPagar/__tests__/update-response.ts b/src/entities/contasPagar/__tests__/update-response.ts
new file mode 100644
index 0000000..ef7eb33
--- /dev/null
+++ b/src/entities/contasPagar/__tests__/update-response.ts
@@ -0,0 +1,25 @@
+export default {
+ id: 12345678
+}
+
+export const updateRequestBody = {
+ vencimento: '2023-01-12',
+ valor: 1500.75,
+ contato: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ },
+ saldo: 100.75,
+ dataEmissao: '2023-01-12',
+ numeroDocumento: '',
+ competencia: '2023-01-12',
+ historico: '',
+ portador: {
+ id: 12345678
+ },
+ categoria: {
+ id: 12345678
+ }
+}
diff --git a/src/entities/contasPagar/index.ts b/src/entities/contasPagar/index.ts
new file mode 100644
index 0000000..e00d673
--- /dev/null
+++ b/src/entities/contasPagar/index.ts
@@ -0,0 +1,155 @@
+import { Entity } from '../@shared/entity'
+import { ICreateBody, ICreateResponse } from './interfaces/create.interface'
+import { IDeleteParams } from './interfaces/delete.interface'
+import {
+ IDownloadBody,
+ IDownloadParams,
+ IDownloadResponse
+} from './interfaces/download.interface'
+import { IFindParams, IFindResponse } from './interfaces/find.interface'
+import { IGetParams, IGetResponse } from './interfaces/get.interface'
+import {
+ IUpdateBody,
+ IUpdateParams,
+ IUpdateResponse
+} from './interfaces/update.interface'
+
+/**
+ * Entidade para interação com Contas a Pagar.
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Pagar
+ */
+export class ContasPagar extends Entity {
+ /**
+ * Remove uma conta a pagar.
+ *
+ * @param {IDeleteParams} params Parâmetros da remoção.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Pagar/delete_contas_pagar__idContaPagar_
+ */
+ public async delete(params: IDeleteParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'contas/pagar',
+ id: String(params.idContaPagar)
+ })
+ }
+
+ /**
+ * Obtém contas a pagar.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Pagar/get_contas_pagar
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'contas/pagar',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite,
+ dataEmissaoInicial: this.prepareStringOrDateParam(
+ params?.dataEmissaoInicial
+ ),
+ dataEmissaoFinal: this.prepareStringOrDateParam(
+ params?.dataEmissaoFinal
+ ),
+ dataVencimentoInicial: this.prepareStringOrDateParam(
+ params?.dataVencimentoInicial
+ ),
+ dataVencimentoFinal: this.prepareStringOrDateParam(
+ params?.dataVencimentoFinal
+ ),
+ dataPagamentoInicial: this.prepareStringOrDateParam(
+ params?.dataPagamentoInicial
+ ),
+ dataPagamentoFinal: this.prepareStringOrDateParam(
+ params?.dataPagamentoFinal
+ ),
+ situacao: params?.situacao,
+ idContato: params?.idContato
+ }
+ })
+ }
+
+ /**
+ * Obtém uma conta a pagar.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Pagar/get_contas_pagar__idContaPagar_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'contas/pagar',
+ id: String(params.idContaPagar)
+ })
+ }
+
+ /**
+ * Cria uma conta a pagar.
+ *
+ * @param {ICreateBody} body O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Pagar/post_contas_pagar
+ */
+ public async create(body: ICreateBody): Promise {
+ return await this.repository.store({
+ endpoint: 'contas/pagar',
+ body
+ })
+ }
+
+ /**
+ * Cria o recebimento de uma conta a pagar.
+ *
+ * @param {IDownloadParams & IDownloadBody} params O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Pagar/post_contas_pagar__idContaPagar__baixar
+ */
+ public async download(
+ params: IDownloadParams & IDownloadBody
+ ): Promise {
+ const { idContaPagar, ...body } = params
+ return await this.repository.store({
+ endpoint: `contas/pagar/${idContaPagar}/baixar`,
+ body
+ })
+ }
+
+ /**
+ * Atualiza uma conta a pagar.
+ *
+ * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização.
+ *
+ * @return {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Pagar/put_contas_pagar__idContaPagar_
+ */
+ public async update(
+ params: IUpdateParams & IUpdateBody
+ ): Promise {
+ const { idContaPagar, ...body } = params
+
+ return await this.repository.replace({
+ endpoint: 'contas/pagar',
+ id: String(idContaPagar),
+ body
+ })
+ }
+}
diff --git a/src/entities/contasPagar/interfaces/create.interface.ts b/src/entities/contasPagar/interfaces/create.interface.ts
new file mode 100644
index 0000000..bdb5e16
--- /dev/null
+++ b/src/entities/contasPagar/interfaces/create.interface.ts
@@ -0,0 +1,64 @@
+interface ContasReceberOcorrenciaUnicaDTO {
+ /**
+ * `1`: Única
+ */
+ tipo: 1
+}
+
+interface ContasReceberOcorrenciaParceladaDTO {
+ /**
+ * `2`: Parcelada
+ */
+ tipo: 2
+ considerarDiasUteis?: boolean
+ diaVencimento: number
+ numeroParcelas?: number
+}
+
+interface ContasReceberOcorrenciaDTO {
+ /**
+ * `3`: Mensal
+ * `4`: Bimestral
+ * `5`: Trimestral
+ * `6`: Semestral
+ * `7`: Anual
+ * `8`: Quinzenal
+ */
+ tipo: 3 | 4 | 5 | 6 | 7 | 8
+ considerarDiasUteis?: boolean
+ diaVencimento: number
+ dataLimite?: string
+}
+
+interface ContasReceberOcorrenciaSemanalDTO {
+ /**
+ * `9`: Semanal
+ */
+ tipo: 9
+ considerarDiasUteis?: boolean
+ diaSemanaVencimento: number
+ dataLimite?: string
+}
+
+export interface ICreateBody {
+ vencimento: string
+ valor: number
+ contato: { id: number }
+ formaPagamento?: { id: number }
+ saldo?: number
+ dataEmissao?: string
+ numeroDocumento?: string
+ competencia?: string
+ historico?: string
+ portador?: { id: number }
+ categoria?: { id: number }
+ ocorrencia?:
+ | ContasReceberOcorrenciaUnicaDTO
+ | ContasReceberOcorrenciaParceladaDTO
+ | ContasReceberOcorrenciaDTO
+ | ContasReceberOcorrenciaSemanalDTO
+}
+
+export interface ICreateResponse {
+ id: number
+}
diff --git a/src/entities/contasPagar/interfaces/delete.interface.ts b/src/entities/contasPagar/interfaces/delete.interface.ts
new file mode 100644
index 0000000..7ea85ce
--- /dev/null
+++ b/src/entities/contasPagar/interfaces/delete.interface.ts
@@ -0,0 +1,3 @@
+export interface IDeleteParams {
+ idContaPagar: number
+}
diff --git a/src/entities/contasPagar/interfaces/download.interface.ts b/src/entities/contasPagar/interfaces/download.interface.ts
new file mode 100644
index 0000000..04047c8
--- /dev/null
+++ b/src/entities/contasPagar/interfaces/download.interface.ts
@@ -0,0 +1,19 @@
+export interface IDownloadParams {
+ idContaPagar: number
+}
+
+export interface IDownloadBody {
+ data: string
+ usarDataVencimento: boolean
+ portador: { id: number }
+ categoria: { id: number }
+ historico: string
+ juros?: number
+ desconto?: number
+ acrescimo?: number
+ valorRecebido?: number
+}
+
+export interface IDownloadResponse {
+ bordero: { id: number }
+}
diff --git a/src/entities/contasPagar/interfaces/find.interface.ts b/src/entities/contasPagar/interfaces/find.interface.ts
new file mode 100644
index 0000000..10dc7f3
--- /dev/null
+++ b/src/entities/contasPagar/interfaces/find.interface.ts
@@ -0,0 +1,26 @@
+import { ISituacao } from '../types/situacao.type'
+
+export interface IFindParams {
+ idContaPagar: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ situacao: ISituacao
+ vencimento: string
+ valor: number
+ contato: { id: number }
+ formaPagamento: { id: number }
+ saldo: number
+ dataEmissao: string
+ vencimentoOriginal: string
+ numeroDocumento: string
+ competencia: string
+ historico: string
+ numeroBanco: string
+ portador: { id: number }
+ categoria: { id: number }
+ borderos: number[]
+ }
+}
diff --git a/src/entities/contasPagar/interfaces/get.interface.ts b/src/entities/contasPagar/interfaces/get.interface.ts
new file mode 100644
index 0000000..b6e6cec
--- /dev/null
+++ b/src/entities/contasPagar/interfaces/get.interface.ts
@@ -0,0 +1,25 @@
+import { ISituacao } from '../types/situacao.type'
+
+export interface IGetParams {
+ pagina?: number
+ limite?: number
+ dataEmissaoInicial?: Date | string
+ dataEmissaoFinal?: Date | string
+ dataVencimentoInicial?: Date | string
+ dataVencimentoFinal?: Date | string
+ dataPagamentoInicial?: Date | string
+ dataPagamentoFinal?: Date | string
+ situacao?: ISituacao
+ idContato?: number
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ situacao: ISituacao
+ vencimento: string
+ valor: number
+ contato: { id: number }
+ formaPagamento: { id: number }
+ }[]
+}
diff --git a/src/entities/contasPagar/interfaces/update.interface.ts b/src/entities/contasPagar/interfaces/update.interface.ts
new file mode 100644
index 0000000..0409d3c
--- /dev/null
+++ b/src/entities/contasPagar/interfaces/update.interface.ts
@@ -0,0 +1,21 @@
+export interface IUpdateParams {
+ idContaPagar: number
+}
+
+export interface IUpdateBody {
+ vencimento: string
+ valor: number
+ contato: { id: number }
+ formaPagamento?: { id: number }
+ saldo?: number
+ dataEmissao?: string
+ numeroDocumento?: string
+ competencia?: string
+ historico?: string
+ portador?: { id: number }
+ categoria?: { id: number }
+}
+
+export interface IUpdateResponse {
+ id: number
+}
diff --git a/src/entities/contasPagar/types/situacao.type.ts b/src/entities/contasPagar/types/situacao.type.ts
new file mode 100644
index 0000000..0d899b0
--- /dev/null
+++ b/src/entities/contasPagar/types/situacao.type.ts
@@ -0,0 +1,12 @@
+/**
+ * Tipagem representativa da situação.
+ *
+ * `1`: Em aberto
+ * `2`: Recebido
+ * `3`: Parcialmente recebido
+ * `4`: Devolvido
+ * `5`: Cancelado
+ * `6`: Devolvido parcial
+ * `7`: Confirmado
+ */
+export type ISituacao = 1 | 2 | 3 | 4 | 5 | 6 | 7
diff --git a/src/entities/contasReceber/__tests__/cancel-bank-slips-response.ts b/src/entities/contasReceber/__tests__/cancel-bank-slips-response.ts
new file mode 100644
index 0000000..8ed70b2
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/cancel-bank-slips-response.ts
@@ -0,0 +1,9 @@
+export default null
+
+export const cancelBankSlipRequest = {
+ type2FA: 1,
+ code2FA: '111111',
+ idOrigem: 16853468718,
+ idDuplicata: 16853468712,
+ reason: 'motivo'
+}
diff --git a/src/entities/contasReceber/__tests__/create-response.ts b/src/entities/contasReceber/__tests__/create-response.ts
new file mode 100644
index 0000000..e7fd29c
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/create-response.ts
@@ -0,0 +1,32 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const createRequestBody = {
+ vencimento: '2023-01-12',
+ valor: 1500.75,
+ contato: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ },
+ dataEmissao: '2023-01-12',
+ numeroDocumento: '',
+ competencia: '2023-01-12',
+ historico: '',
+ portador: {
+ id: 12345678
+ },
+ categoria: {
+ id: 12345678
+ },
+ vendedor: {
+ id: 12345678
+ },
+ ocorrencia: {
+ tipo: 1 as const
+ }
+}
diff --git a/src/entities/contasReceber/__tests__/delete-response.ts b/src/entities/contasReceber/__tests__/delete-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/delete-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/contasReceber/__tests__/download-response.ts b/src/entities/contasReceber/__tests__/download-response.ts
new file mode 100644
index 0000000..edc870c
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/download-response.ts
@@ -0,0 +1,21 @@
+export default {
+ bordero: {
+ id: 12345678
+ }
+}
+
+export const downloadRequestBody = {
+ data: '2023-01-12',
+ usarDataVencimento: false,
+ portador: {
+ id: 12345678
+ },
+ categoria: {
+ id: 12345678
+ },
+ historico: '',
+ juros: 10.5,
+ desconto: 10.5,
+ acrescimo: 10.5,
+ valorRecebido: 100.5
+}
diff --git a/src/entities/contasReceber/__tests__/find-response.ts b/src/entities/contasReceber/__tests__/find-response.ts
new file mode 100644
index 0000000..c391764
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/find-response.ts
@@ -0,0 +1,34 @@
+export default {
+ data: {
+ id: 12345678,
+ situacao: 1,
+ vencimento: '2023-01-12',
+ valor: 1500.75,
+ contato: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ },
+ saldo: 100.75,
+ dataEmissao: '2023-01-12',
+ vencimentoOriginal: '2023-01-12',
+ numeroDocumento: '',
+ competencia: '2023-01-12',
+ historico: '',
+ numeroBanco: '',
+ portador: {
+ id: 12345678
+ },
+ categoria: {
+ id: 12345678
+ },
+ vendedor: {
+ id: 12345678
+ },
+ borderos: [0],
+ ocorrencia: {
+ tipo: 1 as const
+ }
+ }
+}
diff --git a/src/entities/contasReceber/__tests__/get-bank-slips-response.ts b/src/entities/contasReceber/__tests__/get-bank-slips-response.ts
new file mode 100644
index 0000000..beed0a8
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/get-bank-slips-response.ts
@@ -0,0 +1,18 @@
+export default {
+ numberSale: '149',
+ numberNF: '000001',
+ amountAccounts: 1,
+ amountValuesAccounts: 111.2,
+ haveAccountWithIntegration: true,
+ accounts: [
+ {
+ id: 1328793273,
+ idExternal: 'BWbXB',
+ dueDate: '2023-09-12',
+ value: 111.2,
+ situation: 'aberto',
+ iconSituation: 'aberto',
+ descriptionSituation: 'Em aberto'
+ }
+ ]
+}
diff --git a/src/entities/contasReceber/__tests__/get-response.ts b/src/entities/contasReceber/__tests__/get-response.ts
new file mode 100644
index 0000000..5435477
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/get-response.ts
@@ -0,0 +1,16 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ situacao: 1,
+ vencimento: '2023-01-12',
+ valor: 1500.75,
+ contato: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ }
+ }
+ ]
+}
diff --git a/src/entities/contasReceber/__tests__/index.spec.ts b/src/entities/contasReceber/__tests__/index.spec.ts
new file mode 100644
index 0000000..b8d3cea
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/index.spec.ts
@@ -0,0 +1,161 @@
+import { Chance } from 'chance'
+import { ContasReceber } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import cancelBankSlipsResponse, {
+ cancelBankSlipRequest
+} from './cancel-bank-slips-response'
+import createResponse, { createRequestBody } from './create-response'
+import deleteResponse from './delete-response'
+import downloadResponse, { downloadRequestBody } from './download-response'
+import findResponse from './find-response'
+import getBankSlipsResponse from './get-bank-slips-response'
+import getResponse from './get-response'
+import updateResponse, { updateRequestBody } from './update-response'
+
+const chance = Chance()
+
+describe('Contas a receber entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: ContasReceber
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new ContasReceber(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should delete successfully', async () => {
+ const idContaReceber = chance.natural()
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteResponse)
+
+ const response = await entity.delete({ idContaReceber })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/receber',
+ id: String(idContaReceber)
+ })
+ expect(response).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: 'contas/receber',
+ params: {
+ limite: undefined,
+ pagina: undefined,
+ situacoes: undefined,
+ tipoFiltroData: undefined,
+ dataInicial: undefined,
+ dataFinal: undefined,
+ idsCategorias: undefined,
+ idPortador: undefined,
+ idVendedor: undefined,
+ idFormaPagamento: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idContaReceber = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idContaReceber })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/receber',
+ id: String(idContaReceber)
+ })
+ expect(response).toBe(findResponse)
+ })
+
+ it('should get bank slips successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ const idOrigem = chance.natural()
+ repository.setResponse(getBankSlipsResponse)
+
+ const response = await entity.getBankSlips({
+ idOrigem
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/receber/view/bankslips',
+ params: {
+ idOrigem,
+ situations: undefined
+ }
+ })
+ expect(response).toBe(getBankSlipsResponse)
+ })
+
+ it('should create successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(createResponse)
+
+ const response = await entity.create(createRequestBody)
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/receber',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should download successfully', async () => {
+ const idContaReceber = chance.natural()
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(downloadResponse)
+
+ const response = await entity.download({
+ idContaReceber,
+ ...downloadRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: `contas/receber/${idContaReceber}/baixar`,
+ body: downloadRequestBody
+ })
+ expect(response).toBe(downloadResponse)
+ })
+
+ it('should cancel bank slips successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(cancelBankSlipsResponse)
+
+ const response = await entity.cancelBankSlips(cancelBankSlipRequest)
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/receber/cancel/bankslips',
+ body: cancelBankSlipRequest
+ })
+ expect(response).toBe(cancelBankSlipsResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idContaReceber = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idContaReceber,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contas/receber',
+ id: String(idContaReceber),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/contasReceber/__tests__/update-response.ts b/src/entities/contasReceber/__tests__/update-response.ts
new file mode 100644
index 0000000..d372c28
--- /dev/null
+++ b/src/entities/contasReceber/__tests__/update-response.ts
@@ -0,0 +1,29 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const updateRequestBody = {
+ vencimento: '2023-01-12',
+ valor: 1500.75,
+ contato: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ },
+ dataEmissao: '2023-01-12',
+ numeroDocumento: '',
+ competencia: '2023-01-12',
+ historico: '',
+ portador: {
+ id: 12345678
+ },
+ categoria: {
+ id: 12345678
+ },
+ vendedor: {
+ id: 12345678
+ }
+}
diff --git a/src/entities/contasReceber/index.ts b/src/entities/contasReceber/index.ts
new file mode 100644
index 0000000..5646add
--- /dev/null
+++ b/src/entities/contasReceber/index.ts
@@ -0,0 +1,187 @@
+import { Entity } from '../@shared/entity'
+import { ICancelBankSlipsBody } from './interfaces/cancel-bank-slips.interface'
+import { ICreateBody, ICreateResponse } from './interfaces/create.interface'
+import { IDeleteParams } from './interfaces/delete.interface'
+import {
+ IDownloadBody,
+ IDownloadParams,
+ IDownloadResponse
+} from './interfaces/download.interface'
+import { IFindParams, IFindResponse } from './interfaces/find.interface'
+import {
+ IGetBankSlipsParams,
+ IGetBankSlipsResponse
+} from './interfaces/get-bank-slips.interface'
+import { IGetParams, IGetResponse } from './interfaces/get.interface'
+import {
+ IUpdateBody,
+ IUpdateParams,
+ IUpdateResponse
+} from './interfaces/update.interface'
+
+/**
+ * Entidade para interação com Contas a Receber.
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber
+ */
+export class ContasReceber extends Entity {
+ /**
+ * Remove uma conta a receber.
+ *
+ * @param {IDeleteParams} params Parâmetros da remoção.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber/delete_contas_receber__idContaReceber_
+ */
+ public async delete(params: IDeleteParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'contas/receber',
+ id: String(params.idContaReceber)
+ })
+ }
+
+ /**
+ * Obtém contas a receber.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber/get_contas_receber
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'contas/receber',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite,
+ situacoes: params?.situacoes,
+ tipoFiltroData: params?.tipoFiltroData,
+ dataInicial: this.prepareStringOrDateParam(params?.dataInicial),
+ dataFinal: this.prepareStringOrDateParam(params?.dataFinal),
+ idsCategorias: params?.idsCategorias,
+ idPortador: params?.idPortador,
+ idVendedor: params?.idVendedor,
+ idFormaPagamento: params?.idFormaPagamento
+ }
+ })
+ }
+
+ /**
+ * Obtém uma conta a receber.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber/get_contas_receber__idContaReceber_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'contas/receber',
+ id: String(params.idContaReceber)
+ })
+ }
+
+ /**
+ * Obtém os boletos - Bling conta.
+ *
+ * @param {IGetBankSlipsParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber/get_contas_receber_view_bankslips
+ */
+ public async getBankSlips(
+ params: IGetBankSlipsParams
+ ): Promise {
+ return await this.repository.index({
+ endpoint: 'contas/receber/view/bankslips',
+ params: {
+ idOrigem: params.idOrigem,
+ situations: params.situations
+ }
+ })
+ }
+
+ /**
+ * Cria uma conta a receber.
+ *
+ * @param {ICreateBody} body O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber/post_contas_receber
+ */
+ public async create(body: ICreateBody): Promise {
+ return await this.repository.store({
+ endpoint: 'contas/receber',
+ body
+ })
+ }
+
+ /**
+ * Cria o recebimento de uma conta a receber.
+ *
+ * @param {IDownloadParams & IDownloadBody} params O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber/post_contas_receber__idContaReceber__baixar
+ */
+ public async download(
+ params: IDownloadParams & IDownloadBody
+ ): Promise {
+ const { idContaReceber, ...body } = params
+ return await this.repository.store({
+ endpoint: `contas/receber/${idContaReceber}/baixar`,
+ body
+ })
+ }
+
+ /**
+ * Cancelar Boletos - Bling Conta.
+ *
+ * @param {ICancelBankSlipsBody} body Parâmetros do cancelamento.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber/post_contas_receber_cancel_bankslips
+ */
+ public async cancelBankSlips(body: ICancelBankSlipsBody): Promise {
+ return await this.repository.store({
+ endpoint: 'contas/receber/cancel/bankslips',
+ body
+ })
+ }
+
+ /**
+ * Altera uma conta a receber.
+ *
+ * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização.
+ *
+ * @return {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contas%20a%20Receber/put_contas_receber__idContaReceber_
+ */
+ public async update(
+ params: IUpdateParams & IUpdateBody
+ ): Promise {
+ const { idContaReceber, ...body } = params
+
+ return await this.repository.replace({
+ endpoint: 'contas/receber',
+ id: String(idContaReceber),
+ body
+ })
+ }
+}
diff --git a/src/entities/contasReceber/interfaces/cancel-bank-slips.interface.ts b/src/entities/contasReceber/interfaces/cancel-bank-slips.interface.ts
new file mode 100644
index 0000000..e891853
--- /dev/null
+++ b/src/entities/contasReceber/interfaces/cancel-bank-slips.interface.ts
@@ -0,0 +1,37 @@
+interface ContasReceberBankSlipsCancelUnicoDTO {
+ type2FA: number
+ code2FA: string
+ /**
+ * caso for cancelar uma conta sem idOrigem enviar o valor `0`
+ */
+ idOrigem: number
+ idDuplicata: number
+ reason: string
+}
+
+interface ContasReceberBankSlipsCancelTodosDTO {
+ type2FA: number
+ code2FA: string
+ idOrigem: number
+ reason: string
+}
+
+interface ContasReceberBankSlipsCancelUnicoSem2FADTO {
+ /**
+ * caso for cancelar uma conta sem idOrigem enviar o valor `0`
+ */
+ idOrigem: number
+ idDuplicata: number
+ reason: string
+}
+
+interface ContasReceberBankSlipsCancelTodosSem2FADTO {
+ idOrigem: number
+ reason: string
+}
+
+export type ICancelBankSlipsBody =
+ | ContasReceberBankSlipsCancelUnicoDTO
+ | ContasReceberBankSlipsCancelTodosDTO
+ | ContasReceberBankSlipsCancelUnicoSem2FADTO
+ | ContasReceberBankSlipsCancelTodosSem2FADTO
diff --git a/src/entities/contasReceber/interfaces/create.interface.ts b/src/entities/contasReceber/interfaces/create.interface.ts
new file mode 100644
index 0000000..a70c9bc
--- /dev/null
+++ b/src/entities/contasReceber/interfaces/create.interface.ts
@@ -0,0 +1,64 @@
+interface ContasReceberOcorrenciaUnicaDTO {
+ /**
+ * `1`: Única
+ */
+ tipo: 1
+}
+
+interface ContasReceberOcorrenciaParceladaDTO {
+ /**
+ * `2`: Parcelada
+ */
+ tipo: 2
+ considerarDiasUteis?: boolean
+ diaVencimento: number
+ numeroParcelas?: number
+}
+
+interface ContasReceberOcorrenciaDTO {
+ /**
+ * `3`: Mensal
+ * `4`: Bimestral
+ * `5`: Trimestral
+ * `6`: Semestral
+ * `7`: Anual
+ * `8`: Quinzenal
+ */
+ tipo: 3 | 4 | 5 | 6 | 7 | 8
+ considerarDiasUteis?: boolean
+ diaVencimento: number
+ dataLimite?: string
+}
+
+interface ContasReceberOcorrenciaSemanalDTO {
+ /**
+ * `9`: Semanal
+ */
+ tipo: 9
+ considerarDiasUteis?: boolean
+ diaSemanaVencimento: number
+ dataLimite?: string
+}
+
+export interface ICreateBody {
+ vencimento: string
+ valor: number
+ contato: { id: number }
+ formaPagamento?: { id: number }
+ dataEmissao?: string
+ numeroDocumento?: string
+ competencia?: string
+ historico?: string
+ portador?: { id: number }
+ categoria?: { id: number }
+ vendedor?: { id: number }
+ ocorrencia?:
+ | ContasReceberOcorrenciaUnicaDTO
+ | ContasReceberOcorrenciaParceladaDTO
+ | ContasReceberOcorrenciaDTO
+ | ContasReceberOcorrenciaSemanalDTO
+}
+
+export interface ICreateResponse {
+ data: { id: number }
+}
diff --git a/src/entities/contasReceber/interfaces/delete.interface.ts b/src/entities/contasReceber/interfaces/delete.interface.ts
new file mode 100644
index 0000000..ac9ad75
--- /dev/null
+++ b/src/entities/contasReceber/interfaces/delete.interface.ts
@@ -0,0 +1,3 @@
+export interface IDeleteParams {
+ idContaReceber: number
+}
diff --git a/src/entities/contasReceber/interfaces/download.interface.ts b/src/entities/contasReceber/interfaces/download.interface.ts
new file mode 100644
index 0000000..a8bdc7b
--- /dev/null
+++ b/src/entities/contasReceber/interfaces/download.interface.ts
@@ -0,0 +1,19 @@
+export interface IDownloadParams {
+ idContaReceber: number
+}
+
+export interface IDownloadBody {
+ data: string
+ usarDataVencimento: boolean
+ portador: { id: number }
+ categoria: { id: number }
+ historico: string
+ juros?: number
+ desconto?: number
+ acrescimo?: number
+ valorRecebido?: number
+}
+
+export interface IDownloadResponse {
+ bordero: { id: number }
+}
diff --git a/src/entities/contasReceber/interfaces/find.interface.ts b/src/entities/contasReceber/interfaces/find.interface.ts
new file mode 100644
index 0000000..9f4aeae
--- /dev/null
+++ b/src/entities/contasReceber/interfaces/find.interface.ts
@@ -0,0 +1,74 @@
+import { ISituacao } from '../types/situacao.type'
+
+interface ContasReceberOcorrenciaUnicaDTO {
+ /**
+ * `1`: Única
+ */
+ tipo: 1
+}
+
+interface ContasReceberOcorrenciaParceladaDTO {
+ /**
+ * `2`: Parcelada
+ */
+ tipo: 2
+ considerarDiasUteis: boolean
+ diaVencimento: number
+ numeroParcelas: number
+}
+
+interface ContasReceberOcorrenciaDTO {
+ /**
+ * `3`: Mensal
+ * `4`: Bimestral
+ * `5`: Trimestral
+ * `6`: Semestral
+ * `7`: Anual
+ * `8`: Quinzenal
+ */
+ tipo: 3 | 4 | 5 | 6 | 7 | 8
+ considerarDiasUteis: boolean
+ diaVencimento: number
+ dataLimite: string
+}
+
+interface ContasReceberOcorrenciaSemanalDTO {
+ /**
+ * `9`: Semanal
+ */
+ tipo: 9
+ considerarDiasUteis: boolean
+ diaSemanaVencimento: number
+ dataLimite: string
+}
+
+export interface IFindParams {
+ idContaReceber: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ situacao: ISituacao
+ vencimento: string
+ valor: number
+ contato: { id: number }
+ formaPagamento: { id: number }
+ saldo: number
+ dataEmissao: string
+ vencimentoOriginal: string
+ numeroDocumento: string
+ competencia: string
+ historico: string
+ numeroBanco: string
+ portador: { id: number }
+ categoria: { id: number }
+ vendedor: { id: number }
+ borderos: number[]
+ ocorrencia:
+ | ContasReceberOcorrenciaUnicaDTO
+ | ContasReceberOcorrenciaParceladaDTO
+ | ContasReceberOcorrenciaDTO
+ | ContasReceberOcorrenciaSemanalDTO
+ }
+}
diff --git a/src/entities/contasReceber/interfaces/get-bank-slips.interface.ts b/src/entities/contasReceber/interfaces/get-bank-slips.interface.ts
new file mode 100644
index 0000000..34ae20a
--- /dev/null
+++ b/src/entities/contasReceber/interfaces/get-bank-slips.interface.ts
@@ -0,0 +1,23 @@
+import { ISituacaoString } from '../types/situacao.type'
+
+export interface IGetBankSlipsParams {
+ idOrigem: number
+ situations?: ISituacaoString[]
+}
+
+export interface IGetBankSlipsResponse {
+ numberSale: string
+ numberNF: string
+ amountAccounts: number
+ amountValuesAccounts: number
+ haveAccountWithIntegration: true
+ accounts: {
+ id: number
+ idExternal: string
+ dueDate: string
+ value: number
+ situation: ISituacaoString
+ iconSituation: string
+ descriptionSituation: string
+ }[]
+}
diff --git a/src/entities/contasReceber/interfaces/get.interface.ts b/src/entities/contasReceber/interfaces/get.interface.ts
new file mode 100644
index 0000000..7230009
--- /dev/null
+++ b/src/entities/contasReceber/interfaces/get.interface.ts
@@ -0,0 +1,29 @@
+import { ISituacao } from '../types/situacao.type'
+
+export interface IGetParams {
+ pagina?: number
+ limite?: number
+ situacoes?: ISituacao[]
+ /**
+ * `E`: filtrar por data de emissão
+ * `V`: filtrar por data de vencimento
+ */
+ tipoFiltroData?: 'E' | 'V'
+ dataInicial?: Date | string
+ dataFinal?: Date | string
+ idsCategorias?: number[]
+ idPortador?: number
+ idVendedor?: number
+ idFormaPagamento?: number
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ situacao: ISituacao
+ vencimento: string
+ valor: number
+ contato: { id: number }
+ formaPagamento: { id: number }
+ }[]
+}
diff --git a/src/entities/contasReceber/interfaces/update.interface.ts b/src/entities/contasReceber/interfaces/update.interface.ts
new file mode 100644
index 0000000..b855265
--- /dev/null
+++ b/src/entities/contasReceber/interfaces/update.interface.ts
@@ -0,0 +1,23 @@
+export interface IUpdateParams {
+ idContaReceber: number
+}
+
+export interface IUpdateBody {
+ vencimento: string
+ valor: number
+ contato: { id: number }
+ formaPagamento?: { id: number }
+ dataEmissao?: string
+ numeroDocumento?: string
+ competencia?: string
+ historico?: string
+ portador?: { id: number }
+ categoria?: { id: number }
+ vendedor?: { id: number }
+}
+
+export interface IUpdateResponse {
+ data: {
+ id: number
+ }
+}
diff --git a/src/entities/contasReceber/types/situacao.type.ts b/src/entities/contasReceber/types/situacao.type.ts
new file mode 100644
index 0000000..849fb47
--- /dev/null
+++ b/src/entities/contasReceber/types/situacao.type.ts
@@ -0,0 +1,24 @@
+/**
+ * Tipagem representativa da situação.
+ *
+ * `1`: Em aberto
+ * `2`: Recebido
+ * `3`: Parcialmente recebido
+ * `4`: Devolvido
+ * `5`: Cancelado
+ * `6`: Devolvido parcial
+ * `7`: Confirmado
+ */
+export type ISituacao = 1 | 2 | 3 | 4 | 5 | 6 | 7
+
+/**
+ * Tipagem representativa da situação em formato `string`.
+ */
+export type ISituacaoString =
+ | 'aberto'
+ | 'confirmado'
+ | 'pacial'
+ | 'devolvido'
+ | 'devolvidoP'
+ | 'pago'
+ | 'cancelada'
diff --git a/src/entities/contatos/__tests__/change-situation-many-response.ts b/src/entities/contatos/__tests__/change-situation-many-response.ts
new file mode 100644
index 0000000..c6d3786
--- /dev/null
+++ b/src/entities/contatos/__tests__/change-situation-many-response.ts
@@ -0,0 +1,6 @@
+export default null
+
+export const changeSituationManyRequest = {
+ idsContatos: [12345678],
+ situacao: 'A' as const
+}
diff --git a/src/entities/contatos/__tests__/change-situation-response.ts b/src/entities/contatos/__tests__/change-situation-response.ts
new file mode 100644
index 0000000..ff212c0
--- /dev/null
+++ b/src/entities/contatos/__tests__/change-situation-response.ts
@@ -0,0 +1,5 @@
+export default null
+
+export const changeSituationRequest = {
+ situacao: 'A' as const
+}
diff --git a/src/entities/contatos/__tests__/create-response.ts b/src/entities/contatos/__tests__/create-response.ts
new file mode 100644
index 0000000..40e95bb
--- /dev/null
+++ b/src/entities/contatos/__tests__/create-response.ts
@@ -0,0 +1,71 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const createRequestBody = {
+ nome: 'Contato',
+ codigo: 'ASD001',
+ situacao: 'A' as const,
+ numeroDocumento: '12345678910',
+ telefone: '(54) 3333-4444',
+ celular: '(54) 99999-8888',
+ fantasia: 'Nome fantasia',
+ tipo: 'J' as const,
+ indicadorIe: 1 as const,
+ ie: '123.456.789.101',
+ rg: '1234567890',
+ orgaoEmissor: '1234567890',
+ email: 'contato@email.com',
+ endereco: {
+ geral: {
+ endereco: 'R. Olavo Bilac',
+ cep: '95702-000',
+ bairro: 'Imigrante',
+ municipio: 'Bento Gonçalves',
+ uf: 'RS' as const,
+ numero: '914',
+ complemento: 'Sede 101'
+ },
+ cobranca: {
+ endereco: 'R. Olavo Bilac',
+ cep: '95702-000',
+ bairro: 'Imigrante',
+ municipio: 'Bento Gonçalves',
+ uf: 'RS' as const,
+ numero: '914',
+ complemento: 'Sede 101'
+ }
+ },
+ vendedor: {
+ id: 12345678
+ },
+ dadosAdicionais: {
+ dataNascimento: '1990-08-24',
+ sexo: 'M' as const,
+ naturalidade: 'Brasileira'
+ },
+ financeiro: {
+ limiteCredito: 0,
+ condicaoPagamento: '30',
+ categoria: {
+ id: 12345678
+ }
+ },
+ pais: {
+ nome: 'ESTADOS UNIDOS'
+ },
+ tiposContato: [
+ {
+ id: 12345678,
+ descricao: 'Fornecedor'
+ }
+ ],
+ pessoasContato: [
+ {
+ id: 12345678,
+ descricao: 'Fornecedor Fulano'
+ }
+ ]
+}
diff --git a/src/entities/contatos/__tests__/delete-many-response.ts b/src/entities/contatos/__tests__/delete-many-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/contatos/__tests__/delete-many-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/contatos/__tests__/delete-response.ts b/src/entities/contatos/__tests__/delete-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/contatos/__tests__/delete-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/contatos/__tests__/find-response.ts b/src/entities/contatos/__tests__/find-response.ts
new file mode 100644
index 0000000..6e6e3da
--- /dev/null
+++ b/src/entities/contatos/__tests__/find-response.ts
@@ -0,0 +1,68 @@
+export default {
+ data: {
+ id: 12345678,
+ nome: 'Contato',
+ codigo: 'ASD001',
+ situacao: 'A',
+ numeroDocumento: '12345678910',
+ telefone: '(54) 3333-4444',
+ celular: '(54) 99999-8888',
+ fantasia: 'Nome fantasia',
+ tipo: 'J',
+ indicadorIe: 1,
+ ie: '123.456.789.101',
+ rg: '1234567890',
+ orgaoEmissor: '1234567890',
+ email: 'contato@email.com',
+ endereco: {
+ geral: {
+ endereco: 'R. Olavo Bilac',
+ cep: '95702-000',
+ bairro: 'Imigrante',
+ municipio: 'Bento Gonçalves',
+ uf: 'RS',
+ numero: '914',
+ complemento: 'Sede 101'
+ },
+ cobranca: {
+ endereco: 'R. Olavo Bilac',
+ cep: '95702-000',
+ bairro: 'Imigrante',
+ municipio: 'Bento Gonçalves',
+ uf: 'RS',
+ numero: '914',
+ complemento: 'Sede 101'
+ }
+ },
+ vendedor: {
+ id: 12345678
+ },
+ dadosAdicionais: {
+ dataNascimento: '1990-08-24',
+ sexo: 'M',
+ naturalidade: 'Brasileira'
+ },
+ financeiro: {
+ limiteCredito: 0,
+ condicaoPagamento: '30',
+ categoria: {
+ id: 12345678
+ }
+ },
+ pais: {
+ nome: 'ESTADOS UNIDOS'
+ },
+ tiposContato: [
+ {
+ id: 12345678,
+ descricao: 'Fornecedor'
+ }
+ ],
+ pessoasContato: [
+ {
+ id: 12345678,
+ descricao: 'Fornecedor Fulano'
+ }
+ ]
+ }
+}
diff --git a/src/entities/contatos/__tests__/find-types-response.ts b/src/entities/contatos/__tests__/find-types-response.ts
new file mode 100644
index 0000000..08a290a
--- /dev/null
+++ b/src/entities/contatos/__tests__/find-types-response.ts
@@ -0,0 +1,8 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ descricao: 'Fornecedor'
+ }
+ ]
+}
diff --git a/src/entities/contatos/__tests__/get-response.ts b/src/entities/contatos/__tests__/get-response.ts
new file mode 100644
index 0000000..cd43b28
--- /dev/null
+++ b/src/entities/contatos/__tests__/get-response.ts
@@ -0,0 +1,13 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ nome: 'Contato',
+ codigo: 'ASD001',
+ situacao: 'A',
+ 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
new file mode 100644
index 0000000..42d896b
--- /dev/null
+++ b/src/entities/contatos/__tests__/index.spec.ts
@@ -0,0 +1,180 @@
+import { Chance } from 'chance'
+import { Contatos } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import changeSituationManyResponse, {
+ changeSituationManyRequest
+} from './change-situation-many-response'
+import changeSituationResponse, {
+ changeSituationRequest
+} 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 findTypesResponse from './find-types-response'
+import getResponse from './get-response'
+import updateResponse, { updateRequestBody } from './update-response'
+
+const chance = Chance()
+
+describe('Contatos entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: Contatos
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new Contatos(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should delete many successfully', async () => {
+ const idsContatos = []
+ for (let i = 0; i < chance.natural({ min: 2, max: 10 }); i++) {
+ idsContatos.push(chance.natural())
+ }
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteManyResponse)
+
+ const response = await entity.deleteMany({ idsContatos })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos',
+ id: '',
+ params: { idsContatos }
+ })
+ expect(response).toBe(deleteManyResponse)
+ })
+
+ it('should delete successfully', async () => {
+ const idContato = chance.natural()
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteResponse)
+
+ const response = await entity.delete({ idContato })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos',
+ id: String(idContato)
+ })
+ expect(response).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: 'contatos',
+ params: {
+ limite: undefined,
+ pagina: undefined,
+ pesquisa: undefined,
+ criterio: undefined,
+ idTipoContato: undefined,
+ idVendedor: undefined,
+ uf: undefined,
+ telefone: undefined,
+ idsContatos: undefined,
+ numeroDocumento: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idContato = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idContato })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos',
+ id: String(idContato)
+ })
+ expect(response).toBe(findResponse)
+ })
+
+ it('should find types successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idContato = chance.natural()
+ repository.setResponse(findTypesResponse)
+
+ const response = await entity.findTypes({ idContato })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos',
+ id: `${idContato}/tipos`
+ })
+ expect(response).toBe(findTypesResponse)
+ })
+
+ it('should change situation successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idContato = chance.natural()
+ repository.setResponse(changeSituationResponse)
+
+ const response = await entity.changeSituation({
+ idContato,
+ ...changeSituationRequest
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos',
+ id: `${idContato}/situacoes`,
+ body: changeSituationRequest
+ })
+ expect(response).toBe(changeSituationResponse)
+ })
+
+ it('should change situation many successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(changeSituationManyResponse)
+
+ const response = await entity.changeSituationMany(
+ changeSituationManyRequest
+ )
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos/situacoes',
+ body: changeSituationManyRequest
+ })
+ expect(response).toBe(changeSituationResponse)
+ })
+
+ it('should create successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(createResponse)
+
+ const response = await entity.create(createRequestBody)
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idContato = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idContato,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos',
+ id: String(idContato),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/contatos/__tests__/update-response.ts b/src/entities/contatos/__tests__/update-response.ts
new file mode 100644
index 0000000..a1f828a
--- /dev/null
+++ b/src/entities/contatos/__tests__/update-response.ts
@@ -0,0 +1,67 @@
+export default null
+
+export const updateRequestBody = {
+ nome: 'Contato',
+ codigo: 'ASD001',
+ situacao: 'A' as const,
+ numeroDocumento: '12345678910',
+ telefone: '(54) 3333-4444',
+ celular: '(54) 99999-8888',
+ fantasia: 'Nome fantasia',
+ tipo: 'J' as const,
+ indicadorIe: 1 as const,
+ ie: '123.456.789.101',
+ rg: '1234567890',
+ orgaoEmissor: '1234567890',
+ email: 'contato@email.com',
+ endereco: {
+ geral: {
+ endereco: 'R. Olavo Bilac',
+ cep: '95702-000',
+ bairro: 'Imigrante',
+ municipio: 'Bento Gonçalves',
+ uf: 'RS' as const,
+ numero: '914',
+ complemento: 'Sede 101'
+ },
+ cobranca: {
+ endereco: 'R. Olavo Bilac',
+ cep: '95702-000',
+ bairro: 'Imigrante',
+ municipio: 'Bento Gonçalves',
+ uf: 'RS' as const,
+ numero: '914',
+ complemento: 'Sede 101'
+ }
+ },
+ vendedor: {
+ id: 12345678
+ },
+ dadosAdicionais: {
+ dataNascimento: '1990-08-24',
+ sexo: 'M' as const,
+ naturalidade: 'Brasileira'
+ },
+ financeiro: {
+ limiteCredito: 0,
+ condicaoPagamento: '30',
+ categoria: {
+ id: 12345678
+ }
+ },
+ pais: {
+ nome: 'ESTADOS UNIDOS'
+ },
+ tiposContato: [
+ {
+ id: 12345678,
+ descricao: 'Fornecedor'
+ }
+ ],
+ pessoasContato: [
+ {
+ id: 12345678,
+ descricao: 'Fornecedor Fulano'
+ }
+ ]
+}
diff --git a/src/entities/contatos/index.ts b/src/entities/contatos/index.ts
new file mode 100644
index 0000000..885f3b3
--- /dev/null
+++ b/src/entities/contatos/index.ts
@@ -0,0 +1,201 @@
+import { Entity } from '../@shared/entity'
+import { IChangeSituationManyBody } from './interfaces/change-situation-many.interface'
+import {
+ IChangeSituationBody,
+ IChangeSituationParams
+} from './interfaces/change-situation.interface'
+import { ICreateBody, ICreateResponse } from './interfaces/create.interface'
+import { IDeleteManyParams } from './interfaces/delete-many.interface'
+import { IDeleteParams } from './interfaces/delete.interface'
+import {
+ IFindTypesParams,
+ IFindTypesResponse
+} from './interfaces/find-types.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 Contatos.
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos
+ */
+export class Contatos extends Entity {
+ /**
+ * Remove múltiplos contatos.
+ *
+ * @param {IDeleteManyParams} params Parâmetros da remoção.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/delete_contatos
+ */
+ public async deleteMany(params: IDeleteManyParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'contatos',
+ id: String(''),
+ params: {
+ idsContatos: params.idsContatos
+ }
+ })
+ }
+
+ /**
+ * Remove um contato.
+ *
+ * @param {IDeleteParams} params Parâmetros da remoção.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/delete_contatos__idContato_
+ */
+ public async delete(params: IDeleteParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'contatos',
+ id: String(params.idContato)
+ })
+ }
+
+ /**
+ * Obtém contatos.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/get_contatos
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'contatos',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite,
+ pesquisa: params?.pesquisa,
+ criterio: params?.criterio,
+ idTipoContato: params?.idTipoContato,
+ idVendedor: params?.idVendedor,
+ uf: params?.uf,
+ telefone: params?.telefone,
+ idsContatos: params?.idsContatos,
+ numeroDocumento: params?.numeroDocumento
+ }
+ })
+ }
+
+ /**
+ * Obtém um contato.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/get_contatos__idContato_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'contatos',
+ id: String(params.idContato)
+ })
+ }
+
+ /**
+ * Obtém os tipos de contato de um contato.
+ *
+ * @param {IFindTypesParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/get_contatos__idContato__tipos
+ */
+ public async findTypes(
+ params: IFindTypesParams
+ ): Promise {
+ return await this.repository.show({
+ endpoint: 'contatos',
+ id: `${params.idContato}/tipos`
+ })
+ }
+
+ /**
+ * Altera a situação de um contato.
+ *
+ * @param {IChangeSituationParams & IChangeSituationBody} params Os parâmetros para a alteração.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/patch_contatos__idContato__situacoes
+ */
+ public async changeSituation(
+ params: IChangeSituationParams & IChangeSituationBody
+ ): Promise {
+ const { idContato, ...body } = params
+ return await this.repository.replace({
+ endpoint: 'contatos',
+ id: `${idContato}/situacoes`,
+ body
+ })
+ }
+
+ /**
+ * Altera a situação de múltiplos contatos.
+ *
+ * @param {IChangeSituationManyBody} body O corpo da requisição.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/post_contatos_situacoes
+ */
+ public async changeSituationMany(
+ body: IChangeSituationManyBody
+ ): Promise {
+ return await this.repository.store({
+ endpoint: 'contatos/situacoes',
+ body
+ })
+ }
+
+ /**
+ * Cria um contato.
+ *
+ * @param {ICreateBody} body O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/post_contatos
+ */
+ public async create(body: ICreateBody): Promise {
+ return await this.repository.store({
+ endpoint: 'contatos',
+ body
+ })
+ }
+
+ /**
+ * Altera um contato.
+ *
+ * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização.
+ *
+ * @return {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos/put_contatos__idContato_
+ */
+ public async update(params: IUpdateParams & IUpdateBody): Promise {
+ const { idContato, ...body } = params
+
+ return await this.repository.replace({
+ endpoint: 'contatos',
+ id: String(idContato),
+ body
+ })
+ }
+}
diff --git a/src/entities/contatos/interfaces/change-situation-many.interface.ts b/src/entities/contatos/interfaces/change-situation-many.interface.ts
new file mode 100644
index 0000000..78cbd86
--- /dev/null
+++ b/src/entities/contatos/interfaces/change-situation-many.interface.ts
@@ -0,0 +1,6 @@
+import { ISituacao } from '../types/situacao.type'
+
+export interface IChangeSituationManyBody {
+ idsContatos: number[]
+ situacao: ISituacao
+}
diff --git a/src/entities/contatos/interfaces/change-situation.interface.ts b/src/entities/contatos/interfaces/change-situation.interface.ts
new file mode 100644
index 0000000..60a0dc0
--- /dev/null
+++ b/src/entities/contatos/interfaces/change-situation.interface.ts
@@ -0,0 +1,12 @@
+import { ISituacao } from '../types/situacao.type'
+
+export interface IChangeSituationParams {
+ /**
+ * ID do contato
+ */
+ idContato: number
+}
+
+export interface IChangeSituationBody {
+ situacao: ISituacao
+}
diff --git a/src/entities/contatos/interfaces/create.interface.ts b/src/entities/contatos/interfaces/create.interface.ts
new file mode 100644
index 0000000..0c76cb2
--- /dev/null
+++ b/src/entities/contatos/interfaces/create.interface.ts
@@ -0,0 +1,67 @@
+import ITipoPessoa from 'src/entities/@shared/types/tipoPessoa.type'
+import IUF from 'src/entities/@shared/types/uf.type'
+import { IIndicadorIE } from '../types/indicador-ie.type'
+import { ISexo } from '../types/sexo.type'
+import { ISituacao } from '../types/situacao.type'
+
+export interface ICreateBody {
+ nome?: string
+ codigo?: string
+ situacao?: ISituacao
+ numeroDocumento?: string
+ telefone?: string
+ celular?: string
+ fantasia?: string
+ tipo?: ITipoPessoa
+ indicadorIe?: IIndicadorIE
+ ie?: string
+ rg?: string
+ orgaoEmissor?: string
+ email?: string
+ endereco?: {
+ geral?: {
+ endereco?: string
+ cep?: string
+ bairro?: string
+ municipio?: string
+ uf?: IUF
+ numero?: string
+ complemento?: string
+ }
+ cobranca?: {
+ endereco?: string
+ cep?: string
+ bairro?: string
+ municipio?: string
+ uf?: IUF
+ numero?: string
+ complemento?: string
+ }
+ }
+ vendedor?: { id: number }
+ dadosAdicionais?: {
+ dataNascimento?: string
+ sexo?: ISexo
+ naturalidade?: string
+ }
+ financeiro?: {
+ limiteCredito?: number
+ condicaoPagamento?: string
+ categoria?: { id: number }
+ }
+ pais?: { nome?: string }
+ tiposContato?: {
+ id: number
+ descricao?: string
+ }[]
+ pessoasContato?: {
+ id: number
+ descricao?: string
+ }[]
+}
+
+export interface ICreateResponse {
+ data: {
+ id: number
+ }
+}
diff --git a/src/entities/contatos/interfaces/delete-many.interface.ts b/src/entities/contatos/interfaces/delete-many.interface.ts
new file mode 100644
index 0000000..6ebabab
--- /dev/null
+++ b/src/entities/contatos/interfaces/delete-many.interface.ts
@@ -0,0 +1,3 @@
+export interface IDeleteManyParams {
+ idsContatos: number[]
+}
diff --git a/src/entities/contatos/interfaces/delete.interface.ts b/src/entities/contatos/interfaces/delete.interface.ts
new file mode 100644
index 0000000..797425d
--- /dev/null
+++ b/src/entities/contatos/interfaces/delete.interface.ts
@@ -0,0 +1,3 @@
+export interface IDeleteParams {
+ idContato: number
+}
diff --git a/src/entities/contatos/interfaces/find-types.interface.ts b/src/entities/contatos/interfaces/find-types.interface.ts
new file mode 100644
index 0000000..ff604b9
--- /dev/null
+++ b/src/entities/contatos/interfaces/find-types.interface.ts
@@ -0,0 +1,13 @@
+export interface IFindTypesParams {
+ /**
+ * ID do contato
+ */
+ idContato: number
+}
+
+export interface IFindTypesResponse {
+ data: {
+ id: number
+ descricao: string
+ }[]
+}
diff --git a/src/entities/contatos/interfaces/find.interface.ts b/src/entities/contatos/interfaces/find.interface.ts
new file mode 100644
index 0000000..6ce3d8f
--- /dev/null
+++ b/src/entities/contatos/interfaces/find.interface.ts
@@ -0,0 +1,71 @@
+import ITipoPessoa from 'src/entities/@shared/types/tipoPessoa.type'
+import IUF from 'src/entities/@shared/types/uf.type'
+import { IIndicadorIE } from '../types/indicador-ie.type'
+import { ISexo } from '../types/sexo.type'
+import { ISituacao } from '../types/situacao.type'
+
+export interface IFindParams {
+ /**
+ * ID do contato
+ */
+ idContato: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ nome: string
+ codigo: string
+ situacao: ISituacao
+ numeroDocumento: string
+ telefone: string
+ celular: string
+ fantasia: string
+ tipo: ITipoPessoa
+ indicadorIe: IIndicadorIE
+ ie: string
+ rg: string
+ orgaoEmissor: string
+ email: string
+ endereco: {
+ geral: {
+ endereco: string
+ cep: string
+ bairro: string
+ municipio: string
+ uf: IUF
+ numero: string
+ complemento: string
+ }
+ cobranca: {
+ endereco: string
+ cep: string
+ bairro: string
+ municipio: string
+ uf: IUF
+ numero: string
+ complemento: string
+ }
+ }
+ vendedor: { id: number }
+ dadosAdicionais: {
+ dataNascimento: string
+ sexo: ISexo
+ naturalidade: string
+ }
+ financeiro: {
+ limiteCredito: 0
+ condicaoPagamento: string
+ categoria: { id: number }
+ }
+ pais: { nome: string }
+ tiposContato: {
+ id: number
+ descricao: string
+ }[]
+ pessoasContato: {
+ id: number
+ descricao: string
+ }[]
+ }
+}
diff --git a/src/entities/contatos/interfaces/get.interface.ts b/src/entities/contatos/interfaces/get.interface.ts
new file mode 100644
index 0000000..8260a4c
--- /dev/null
+++ b/src/entities/contatos/interfaces/get.interface.ts
@@ -0,0 +1,58 @@
+import IUF from 'src/entities/@shared/types/uf.type'
+import { ICriterio } from '../types/criterio.type'
+import { ISituacao } from '../types/situacao.type'
+
+export interface IGetParams {
+ /**
+ * N° da página da listagem
+ */
+ pagina?: number
+ /**
+ * Quantidade de registros que devem ser exibidos por página
+ */
+ limite?: number
+ /**
+ * Nome, CPF/CNPJ, fantasia, e-mail ou código do contato
+ */
+ pesquisa?: string
+ /**
+ * Criterio de listagem
+ */
+ criterio?: ICriterio
+ /**
+ * ID do tipo do contato
+ */
+ idTipoContato?: number
+ /**
+ * ID do vendedor relacionado ao contato
+ */
+ idVendedor?: number
+ /**
+ * UF do contato
+ */
+ uf?: IUF
+ /**
+ * Telefone do contato
+ */
+ telefone?: string
+ /**
+ * IDs dos contatos
+ */
+ idsContatos?: number[]
+ /**
+ * CPF/CNPJ, desconsiderando a pontuação
+ */
+ numeroDocumento?: string
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ nome: string
+ codigo: string
+ situacao: ISituacao
+ numeroDocumento: string
+ telefone: string
+ celular: string
+ }[]
+}
diff --git a/src/entities/contatos/interfaces/update.interface.ts b/src/entities/contatos/interfaces/update.interface.ts
new file mode 100644
index 0000000..d17937c
--- /dev/null
+++ b/src/entities/contatos/interfaces/update.interface.ts
@@ -0,0 +1,68 @@
+import ITipoPessoa from 'src/entities/@shared/types/tipoPessoa.type'
+import IUF from 'src/entities/@shared/types/uf.type'
+import { IIndicadorIE } from '../types/indicador-ie.type'
+import { ISexo } from '../types/sexo.type'
+import { ISituacao } from '../types/situacao.type'
+
+export interface IUpdateParams {
+ /**
+ * ID do contato
+ */
+ idContato: number
+}
+
+export interface IUpdateBody {
+ nome?: string
+ codigo?: string
+ situacao?: ISituacao
+ numeroDocumento?: string
+ telefone?: string
+ celular?: string
+ fantasia?: string
+ tipo?: ITipoPessoa
+ indicadorIe?: IIndicadorIE
+ ie?: string
+ rg?: string
+ orgaoEmissor?: string
+ email?: string
+ endereco?: {
+ geral?: {
+ endereco?: string
+ cep?: string
+ bairro?: string
+ municipio?: string
+ uf?: IUF
+ numero?: string
+ complemento?: string
+ }
+ cobranca?: {
+ endereco?: string
+ cep?: string
+ bairro?: string
+ municipio?: string
+ uf?: IUF
+ numero?: string
+ complemento?: string
+ }
+ }
+ vendedor?: { id: number }
+ dadosAdicionais?: {
+ dataNascimento?: string
+ sexo?: ISexo
+ naturalidade?: string
+ }
+ financeiro?: {
+ limiteCredito?: number
+ condicaoPagamento?: string
+ categoria?: { id: number }
+ }
+ pais?: { nome?: string }
+ tiposContato?: {
+ id: number
+ descricao?: string
+ }[]
+ pessoasContato?: {
+ id: number
+ descricao?: string
+ }[]
+}
diff --git a/src/entities/contatos/types/criterio.type.ts b/src/entities/contatos/types/criterio.type.ts
new file mode 100644
index 0000000..5578e8d
--- /dev/null
+++ b/src/entities/contatos/types/criterio.type.ts
@@ -0,0 +1,9 @@
+/**
+ * Tipagem para critério de listagem.
+ *
+ * - `1`: Todos
+ * - `2`: Excluídos
+ * - `3`: Últimos incluídos
+ * - `4`: Sem movimento
+ */
+export type ICriterio = 1 | 2 | 3 | 4
diff --git a/src/entities/contatos/types/indicador-ie.type.ts b/src/entities/contatos/types/indicador-ie.type.ts
new file mode 100644
index 0000000..7c87d7e
--- /dev/null
+++ b/src/entities/contatos/types/indicador-ie.type.ts
@@ -0,0 +1,8 @@
+/**
+ * Tipagem referente ao indicador de inscrição estadual.
+ *
+ * - `1`: Contribuinte ICMS
+ * - `2`: Contribuinte isento de Inscrição no cadastro de Contribuintes
+ * - `9`: Não Contribuinte
+ */
+export type IIndicadorIE = 1 | 2 | 9
diff --git a/src/entities/contatos/types/sexo.type.ts b/src/entities/contatos/types/sexo.type.ts
new file mode 100644
index 0000000..f8e6f41
--- /dev/null
+++ b/src/entities/contatos/types/sexo.type.ts
@@ -0,0 +1,7 @@
+/**
+ * Tipagem referente ao sexo do contato.
+ *
+ * - `M`: Masculino
+ * - `F`: Feminino
+ */
+export type ISexo = 'M' | 'F'
diff --git a/src/entities/contatos/types/situacao.type.ts b/src/entities/contatos/types/situacao.type.ts
new file mode 100644
index 0000000..fb3951b
--- /dev/null
+++ b/src/entities/contatos/types/situacao.type.ts
@@ -0,0 +1,9 @@
+/**
+ * Tipagem para a situação do contato.
+ *
+ * - `A`: Ativo
+ * - `E`: Excluído
+ * - `I`: Inativo
+ * - `S`: Sem movimentação
+ */
+export type ISituacao = 'A' | 'E' | 'I' | 'S'
diff --git a/src/entities/contatosTipos/__tests__/get-response.ts b/src/entities/contatosTipos/__tests__/get-response.ts
new file mode 100644
index 0000000..08a290a
--- /dev/null
+++ b/src/entities/contatosTipos/__tests__/get-response.ts
@@ -0,0 +1,8 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ descricao: 'Fornecedor'
+ }
+ ]
+}
diff --git a/src/entities/contatosTipos/__tests__/index.spec.ts b/src/entities/contatosTipos/__tests__/index.spec.ts
new file mode 100644
index 0000000..33502b8
--- /dev/null
+++ b/src/entities/contatosTipos/__tests__/index.spec.ts
@@ -0,0 +1,29 @@
+import { ContatosTipos } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import getResponse from './get-response'
+
+describe('Contatos - Tipos entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: ContatosTipos
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new ContatosTipos(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should get successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getResponse)
+
+ const response = await entity.get()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contatos/tipos'
+ })
+ expect(response).toBe(getResponse)
+ })
+})
diff --git a/src/entities/contatosTipos/index.ts b/src/entities/contatosTipos/index.ts
new file mode 100644
index 0000000..b9c01f5
--- /dev/null
+++ b/src/entities/contatosTipos/index.ts
@@ -0,0 +1,23 @@
+import { Entity } from '../@shared/entity'
+import { IGetResponse } from './interfaces/get.interface'
+
+/**
+ * Entidade para interação com Contatos - Tipos.
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos%20-%20Tipos
+ */
+export class ContatosTipos extends Entity {
+ /**
+ * Obtém tipos de contato.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contatos%20-%20Tipos/get_contatos_tipos
+ */
+ public async get(): Promise {
+ return await this.repository.index({
+ endpoint: 'contatos/tipos'
+ })
+ }
+}
diff --git a/src/entities/contatosTipos/interfaces/get.interface.ts b/src/entities/contatosTipos/interfaces/get.interface.ts
new file mode 100644
index 0000000..6202c6c
--- /dev/null
+++ b/src/entities/contatosTipos/interfaces/get.interface.ts
@@ -0,0 +1,6 @@
+export interface IGetResponse {
+ data: {
+ id: number
+ descricao: string
+ }[]
+}
diff --git a/src/entities/contatosTipos/types/criterio.type.ts b/src/entities/contatosTipos/types/criterio.type.ts
new file mode 100644
index 0000000..5578e8d
--- /dev/null
+++ b/src/entities/contatosTipos/types/criterio.type.ts
@@ -0,0 +1,9 @@
+/**
+ * Tipagem para critério de listagem.
+ *
+ * - `1`: Todos
+ * - `2`: Excluídos
+ * - `3`: Últimos incluídos
+ * - `4`: Sem movimento
+ */
+export type ICriterio = 1 | 2 | 3 | 4
diff --git a/src/entities/contatosTipos/types/indicador-ie.type.ts b/src/entities/contatosTipos/types/indicador-ie.type.ts
new file mode 100644
index 0000000..7c87d7e
--- /dev/null
+++ b/src/entities/contatosTipos/types/indicador-ie.type.ts
@@ -0,0 +1,8 @@
+/**
+ * Tipagem referente ao indicador de inscrição estadual.
+ *
+ * - `1`: Contribuinte ICMS
+ * - `2`: Contribuinte isento de Inscrição no cadastro de Contribuintes
+ * - `9`: Não Contribuinte
+ */
+export type IIndicadorIE = 1 | 2 | 9
diff --git a/src/entities/contatosTipos/types/sexo.type.ts b/src/entities/contatosTipos/types/sexo.type.ts
new file mode 100644
index 0000000..f8e6f41
--- /dev/null
+++ b/src/entities/contatosTipos/types/sexo.type.ts
@@ -0,0 +1,7 @@
+/**
+ * Tipagem referente ao sexo do contato.
+ *
+ * - `M`: Masculino
+ * - `F`: Feminino
+ */
+export type ISexo = 'M' | 'F'
diff --git a/src/entities/contatosTipos/types/situacao.type.ts b/src/entities/contatosTipos/types/situacao.type.ts
new file mode 100644
index 0000000..fb3951b
--- /dev/null
+++ b/src/entities/contatosTipos/types/situacao.type.ts
@@ -0,0 +1,9 @@
+/**
+ * Tipagem para a situação do contato.
+ *
+ * - `A`: Ativo
+ * - `E`: Excluído
+ * - `I`: Inativo
+ * - `S`: Sem movimentação
+ */
+export type ISituacao = 'A' | 'E' | 'I' | 'S'
diff --git a/src/entities/contracts.ts b/src/entities/contracts.ts
deleted file mode 100644
index 93f3a4a..0000000
--- a/src/entities/contracts.ts
+++ /dev/null
@@ -1,176 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import All from '../core/functions/all'
-import Find from '../core/functions/find'
-import FindBy from '../core/functions/findBy'
-import Create from '../core/functions/create'
-import Update from '../core/functions/update'
-import Delete from '../core/functions/delete'
-
-type IContractSituacao = 'A' | 'I' | 'B' | 'S' | 'T'
-
-export interface IContract {
- dataCriacao?: string
- dataBase?: string
- contatoDiferenteCobranca?: number
- numeroContrato?: string
- descricao?: string
- situacao: IContractSituacao
- valor: number
- emiteNota: 'S' | 'N'
- periodicidadeCobranca?: string
- opcoesNota?: {
- percentualISS?: number
- descISSTotalNota: 'S' | 'N'
- descIRTotalNota?: 'S' | 'N'
- codListaServico?: string
- idProdutoVinculado?: string
- mesNota?: string
- textoNota?: string
- naturezaOperacao?: string
- cfop?: string
- }
- idCategoria?: number
- idPortador?: number
- idVendedor?: number
- desconto?: number
- mesFimDesconto?: string
- anoFimDesconto?: string
- mesTermino?: string
- anoTermino?: string
- mesVencimento?: string
- nroParcelasVendedor?: number
- percentualVendedor?: number
- emiteOS?: 'S' | 'N'
- obs?: string
- cliente: {
- nome?: string
- cnpj_cpf: string
- tipo: 'F' | 'J'
- ie_rg?: string
- rg?: string
- endereco?: string
- numero?: string
- complemento?: string
- cidade?: string
- bairro?: string
- cep?: string
- uf?: string
- email?: string
- fone?: string
- celular?: string
- }
- contato?: {
- nome?: string
- cnpj_cpf: string
- tipoPessoa?: 'F' | 'J'
- ie_rg?: string
- rg?: string
- endereco?: string
- numero?: string
- complemento?: string
- cidade?: string
- bairro?: string
- cep?: string
- uf?: string
- email?: string
- fone?: string
- celular?: string
- }
- anexos: {
- anexo: {
- filename: string
- data: string
- }
- }[]
- diaVencimento?: string
-}
-
-export interface IContractFilters {
- dataCriacao?: string
- dataBase?: string
- situacao?: IContractSituacao
- idContato?: number
- idCliente?: number
-}
-
-export type IContractInfos = Record
-
-export interface IContractCreateResponse {
- id: string
- numeroContrato: string
-}
-
-export interface IContractDeleteResponse {
- id: string
- mensagem: string
-}
-
-export interface IContractResponse {
- id: string
- nome: string
- descricao: string
- contatoDiferenteCobranca: string
- numeroContrato: string
- idCliente: string
- idContato: string
- situacao: IContractSituacao
- dataCriacao: string
- valor: string
- dataBase: string
- mesVencimento: string
- diaVencimento: string
- periodicidadeCobranca: string
- emiteNota: 'S' | 'N'
- tipoManutencao: string
- idCategoria: string
- idPortador: string
- idFormaPagamento?: string
- desconto: string
- mesFimDesconto: string
- anoFimDesconto: string
- mesTermino: string
- anoTermino: string
- idVendedor: string
- nroParcelasVendedor: string
- percentualVendedor: string
- emiteOS: 'S' | 'N'
- obs: string
- dataUltimoPagamento?: string
- opcoesNota: {
- percentualISS: string
- descontarISS?: string
- descISSTotalNota: 'S' | 'N'
- descIRTotalNota: 'S' | 'N'
- codListaServico: string
- idProdutoVinculado: string
- mesNota: string
- textoNota: string
- naturezaOperacao: string
- cfop?: string
- }
- nroContasEmAtraso: string
- anexos: {
- nome: string
- link: string
- }[]
-}
-
-export default function Contracts (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'contrato',
- pluralName: 'contratos'
- }
-
- return Object.assign(config, {
- all: new All().all,
- find: new Find().find,
- findBy: new FindBy()
- .findBy,
- create: new Create().create,
- update: new Update().update,
- delete: new Delete().delete
- })
-}
diff --git a/src/entities/contratos/__tests__/create-response.ts b/src/entities/contratos/__tests__/create-response.ts
new file mode 100644
index 0000000..6ed9f47
--- /dev/null
+++ b/src/entities/contratos/__tests__/create-response.ts
@@ -0,0 +1,68 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const createRequestBody = {
+ descricao: 'Alugel do apartamento A102',
+ data: '2023-02-19',
+ numero: '25',
+ valor: 59.99,
+ situacao: 1 as const,
+ contato: {
+ id: 12345678
+ },
+ dataFim: '2024-05',
+ tipoManutencao: 1 as const,
+ emitirOrdemServico: false,
+ observacoes: '',
+ vendedor: {
+ id: 12345678,
+ comissao: {
+ aliquota: 0.5,
+ numeroParcelas: 1
+ }
+ },
+ categoria: {
+ id: 12345678
+ },
+ desconto: {
+ valor: 4.99,
+ dataFim: '2023-02'
+ },
+ contaContabil: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ },
+ notaFiscal: {
+ mes: 2 as const,
+ gerar: 1 as const,
+ descontarImpostoRenda: 1 as const,
+ texto: 'Exemplo de texto.',
+ cfop: '5.556',
+ iss: {
+ descontar: false,
+ aliquota: 2.5
+ },
+ item: {
+ codigoServico: '14.13',
+ produto: {
+ id: 12345678
+ }
+ }
+ },
+ cobranca: {
+ dataBase: '2023-02-22',
+ contato: {
+ id: 12345678
+ },
+ vencimento: {
+ tipo: 1 as const,
+ dia: 10,
+ periodicidade: 1 as const
+ }
+ }
+}
diff --git a/src/entities/contratos/__tests__/delete-response.ts b/src/entities/contratos/__tests__/delete-response.ts
new file mode 100644
index 0000000..7b85954
--- /dev/null
+++ b/src/entities/contratos/__tests__/delete-response.ts
@@ -0,0 +1 @@
+export default null
diff --git a/src/entities/contratos/__tests__/find-response.ts b/src/entities/contratos/__tests__/find-response.ts
new file mode 100644
index 0000000..91e0855
--- /dev/null
+++ b/src/entities/contratos/__tests__/find-response.ts
@@ -0,0 +1,65 @@
+export default {
+ data: {
+ id: 123455678,
+ descricao: 'Alugel do apartamento A102',
+ data: '2023-02-19',
+ numero: '25',
+ valor: 59.99,
+ situacao: 1 as const,
+ contato: {
+ id: 12345678
+ },
+ dataFim: '2024-05',
+ tipoManutencao: 1 as const,
+ emitirOrdemServico: false,
+ observacoes: '',
+ vendedor: {
+ id: 12345678,
+ comissao: {
+ aliquota: 0.5,
+ numeroParcelas: 1
+ }
+ },
+ categoria: {
+ id: 12345678
+ },
+ desconto: {
+ valor: 4.99,
+ dataFim: '2023-02'
+ },
+ contaContabil: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ },
+ notaFiscal: {
+ mes: 2 as const,
+ gerar: 1 as const,
+ descontarImpostoRenda: 1 as const,
+ texto: 'Exemplo de texto.',
+ cfop: '5.556',
+ iss: {
+ descontar: false,
+ aliquota: 2.5
+ },
+ item: {
+ codigoServico: '14.13',
+ produto: {
+ id: 12345678
+ }
+ }
+ },
+ cobranca: {
+ dataBase: '2023-02-22',
+ contato: {
+ id: 12345678
+ },
+ vencimento: {
+ tipo: 1 as const,
+ dia: 10,
+ periodicidade: 1 as const
+ }
+ }
+ }
+}
diff --git a/src/entities/contratos/__tests__/get-response.ts b/src/entities/contratos/__tests__/get-response.ts
new file mode 100644
index 0000000..415d6b9
--- /dev/null
+++ b/src/entities/contratos/__tests__/get-response.ts
@@ -0,0 +1,15 @@
+export default {
+ data: [
+ {
+ id: 123455678,
+ descricao: 'Alugel do apartamento A102',
+ data: '2023-02-19',
+ numero: '25',
+ valor: 59.99,
+ situacao: 1,
+ contato: {
+ id: 12345678
+ }
+ }
+ ]
+}
diff --git a/src/entities/contratos/__tests__/index.spec.ts b/src/entities/contratos/__tests__/index.spec.ts
new file mode 100644
index 0000000..17f84e3
--- /dev/null
+++ b/src/entities/contratos/__tests__/index.spec.ts
@@ -0,0 +1,106 @@
+import { Chance } from 'chance'
+import { Contratos } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import createResponse, { createRequestBody } from './create-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('Contratos entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: Contratos
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new Contratos(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should delete successfully', async () => {
+ const idContrato = chance.natural()
+ const spy = jest.spyOn(repository, 'destroy')
+ repository.setResponse(deleteResponse)
+
+ const response = await entity.delete({ idContrato })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contratos',
+ id: String(idContrato)
+ })
+ expect(response).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: 'contratos',
+ params: {
+ limite: undefined,
+ pagina: undefined,
+ dataCriacaoInicio: undefined,
+ dataCriacaoFinal: undefined,
+ dataBaseInicio: undefined,
+ dataBaseFinal: undefined,
+ situacao: undefined,
+ idContato: undefined,
+ idContatoCobranca: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idContrato = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idContrato })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contratos',
+ id: String(idContrato)
+ })
+ expect(response).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: 'contratos',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idContrato = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idContrato,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'contratos',
+ id: String(idContrato),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/contratos/__tests__/update-response.ts b/src/entities/contratos/__tests__/update-response.ts
new file mode 100644
index 0000000..dd32e3c
--- /dev/null
+++ b/src/entities/contratos/__tests__/update-response.ts
@@ -0,0 +1,68 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const updateRequestBody = {
+ descricao: 'Alugel do apartamento A102',
+ data: '2023-02-19',
+ numero: '25',
+ valor: 59.99,
+ situacao: 1 as const,
+ contato: {
+ id: 12345678
+ },
+ dataFim: '2024-05',
+ tipoManutencao: 1 as const,
+ emitirOrdemServico: false,
+ observacoes: '',
+ vendedor: {
+ id: 12345678,
+ comissao: {
+ aliquota: 0.5,
+ numeroParcelas: 1
+ }
+ },
+ categoria: {
+ id: 12345678
+ },
+ desconto: {
+ valor: 4.99,
+ dataFim: '2023-02'
+ },
+ contaContabil: {
+ id: 12345678
+ },
+ formaPagamento: {
+ id: 12345678
+ },
+ notaFiscal: {
+ mes: 2 as const,
+ gerar: 1 as const,
+ descontarImpostoRenda: 1 as const,
+ texto: 'Exemplo de texto.',
+ cfop: '5.556',
+ iss: {
+ descontar: false,
+ aliquota: 2.5
+ },
+ item: {
+ codigoServico: '14.13',
+ produto: {
+ id: 12345678
+ }
+ }
+ },
+ cobranca: {
+ dataBase: '2023-02-22',
+ contato: {
+ id: 12345678
+ },
+ vencimento: {
+ tipo: 1 as const,
+ dia: 10,
+ periodicidade: 1 as const
+ }
+ }
+}
diff --git a/src/entities/contratos/index.ts b/src/entities/contratos/index.ts
new file mode 100644
index 0000000..a556af7
--- /dev/null
+++ b/src/entities/contratos/index.ts
@@ -0,0 +1,121 @@
+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 { IGetParams, IGetResponse } from './interfaces/get.interface'
+import {
+ IUpdateBody,
+ IUpdateParams,
+ IUpdateResponse
+} from './interfaces/update.interface'
+
+/**
+ * Entidade para interação com contratos.
+ *
+ * @see https://developer.bling.com.br/referencia#/Contratos
+ */
+export class Contratos extends Entity {
+ /**
+ * Remove um contrato.
+ *
+ * @param {IDeleteParams} params Parâmetros da remoção.
+ *
+ * @returns {Promise} Não há retorno.
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contratos/delete_contratos__idContrato_
+ */
+ public async delete(params: IDeleteParams): Promise {
+ return await this.repository.destroy({
+ endpoint: 'contratos',
+ id: String(params.idContrato)
+ })
+ }
+
+ /**
+ * Obtém contratos.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contratos/get_contratos
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'contratos',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite,
+ dataCriacaoInicio: this.prepareStringOrDateParam(
+ params?.dataCriacaoInicio
+ ),
+ dataCriacaoFinal: this.prepareStringOrDateParam(
+ params?.dataCriacaoFinal
+ ),
+ dataBaseInicio: this.prepareStringOrDateParam(params?.dataBaseInicio),
+ dataBaseFinal: this.prepareStringOrDateParam(params?.dataBaseFinal),
+ situacao: params?.situacao,
+ idContato: params?.idContato,
+ idContatoCobranca: params?.idContatoCobranca
+ }
+ })
+ }
+
+ /**
+ * Obtém um contrato.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contratos/get_contratos__idContrato_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'contratos',
+ id: String(params.idContrato)
+ })
+ }
+
+ /**
+ * Cria um contrato.
+ *
+ * @param {ICreateBody} body O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contratos/post_contratos
+ */
+ public async create(body: ICreateBody): Promise {
+ return await this.repository.store({
+ endpoint: 'contratos',
+ body
+ })
+ }
+
+ /**
+ * Altera um contrato.
+ *
+ * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização.
+ *
+ * @return {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Contratos/put_contratos__idContrato_
+ */
+ public async update(
+ params: IUpdateParams & IUpdateBody
+ ): Promise {
+ const { idContrato, ...body } = params
+
+ return await this.repository.replace({
+ endpoint: 'contratos',
+ id: String(idContrato),
+ body
+ })
+ }
+}
diff --git a/src/entities/contratos/interfaces/create.interface.ts b/src/entities/contratos/interfaces/create.interface.ts
new file mode 100644
index 0000000..ecf5171
--- /dev/null
+++ b/src/entities/contratos/interfaces/create.interface.ts
@@ -0,0 +1,62 @@
+import { INotaFiscalDescontarImpostoRenda } from '../types/nota-fiscal-descontar-imposto-renda.type'
+import { INotaFiscalGerar } from '../types/nota-fiscal-gerar.type'
+import { INotaFiscalMes } from '../types/nota-fiscal-mes.type'
+import { IPeriodicidade } from '../types/periodicidade.type'
+import { ISituacao } from '../types/situacao.type'
+import { ITipoManutencao } from '../types/tipo-manutencao.type'
+import { ITipoVencimento } from '../types/tipo-vencimento.type'
+
+export interface ICreateBody {
+ descricao: string
+ data: string
+ numero: string
+ valor: number
+ situacao: ISituacao
+ contato: { id: number }
+ dataFim: string
+ tipoManutencao: ITipoManutencao
+ emitirOrdemServico: boolean
+ observacoes: string
+ vendedor: {
+ id: number
+ comissao: {
+ aliquota: number
+ numeroParcelas: number
+ }
+ }
+ categoria: { id: number }
+ desconto: {
+ valor: number
+ dataFim: string
+ }
+ contaContabil: { id: number }
+ formaPagamento: { id: number }
+ notaFiscal?: {
+ mes?: INotaFiscalMes
+ gerar?: INotaFiscalGerar
+ descontarImpostoRenda?: INotaFiscalDescontarImpostoRenda
+ texto?: string
+ cfop?: string
+ iss?: {
+ descontar?: boolean
+ aliquota?: number
+ }
+ item?: {
+ codigoServico?: string
+ produto?: { id: number }
+ }
+ }
+ cobranca: {
+ dataBase?: string
+ contato?: { id: number }
+ vencimento?: {
+ tipo?: ITipoVencimento
+ dia?: number
+ periodicidade?: IPeriodicidade
+ }
+ }
+}
+
+export interface ICreateResponse {
+ data: { id: number }
+}
diff --git a/src/entities/contratos/interfaces/delete.interface.ts b/src/entities/contratos/interfaces/delete.interface.ts
new file mode 100644
index 0000000..7a30af3
--- /dev/null
+++ b/src/entities/contratos/interfaces/delete.interface.ts
@@ -0,0 +1,6 @@
+export interface IDeleteParams {
+ /**
+ * ID do contrato
+ */
+ idContrato: number
+}
diff --git a/src/entities/contratos/interfaces/find.interface.ts b/src/entities/contratos/interfaces/find.interface.ts
new file mode 100644
index 0000000..4d0133f
--- /dev/null
+++ b/src/entities/contratos/interfaces/find.interface.ts
@@ -0,0 +1,72 @@
+import { INotaFiscalDescontarImpostoRenda } from '../types/nota-fiscal-descontar-imposto-renda.type'
+import { INotaFiscalGerar } from '../types/nota-fiscal-gerar.type'
+import { INotaFiscalMes } from '../types/nota-fiscal-mes.type'
+import { IPeriodicidade } from '../types/periodicidade.type'
+import { ISituacao } from '../types/situacao.type'
+import { ITipoManutencao } from '../types/tipo-manutencao.type'
+import { ITipoVencimento } from '../types/tipo-vencimento.type'
+
+export interface IFindParams {
+ /**
+ * ID do contrato
+ */
+ idContrato: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ descricao: string
+ data: string
+ numero: string
+ valor: number
+ situacao: ISituacao
+ contato: { id: number }
+ dataFim: string
+ tipoManutencao: ITipoManutencao
+ emitirOrdemServico: boolean
+ observacoes: string
+ vendedor: {
+ id: number
+ comissao: {
+ aliquota: number
+ numeroParcelas: number
+ }
+ }
+ categoria: { id: number }
+ desconto: {
+ valor: number
+ dataFim: string
+ }
+ contaContabil: { id: number }
+ formaPagamento: { id: number }
+ notaFiscal: {
+ mes: INotaFiscalMes
+ gerar: INotaFiscalGerar
+ descontarImpostoRenda: INotaFiscalDescontarImpostoRenda
+ texto: string
+ cfop: string
+ iss: {
+ descontar: boolean
+ aliquota: number
+ }
+ item: {
+ codigoServico: string
+ produto: { id: number }
+ }
+ }
+ cobranca: {
+ dataBase: string
+ contato: { id: number }
+ vencimento: {
+ tipo: ITipoVencimento
+ /**
+ * Caso o dia informado não exista em um determinado mês(29, 30, 31), o
+ * vencimento da cobrança utilizará o ultimo dia válido do mês.
+ */
+ dia: number
+ periodicidade: IPeriodicidade
+ }
+ }
+ }
+}
diff --git a/src/entities/contratos/interfaces/get.interface.ts b/src/entities/contratos/interfaces/get.interface.ts
new file mode 100644
index 0000000..57c2dfe
--- /dev/null
+++ b/src/entities/contratos/interfaces/get.interface.ts
@@ -0,0 +1,52 @@
+import { ISituacao } from '../types/situacao.type'
+
+export interface IGetParams {
+ /**
+ * N° da página da listagem
+ */
+ pagina?: number
+ /**
+ * Quantidade de registros que devem ser exibidos por página
+ */
+ limite?: number
+ /**
+ * Data inicial de criação
+ */
+ dataCriacaoInicio?: Date | string
+ /**
+ * Data final de criação
+ */
+ dataCriacaoFinal?: Date | string
+ /**
+ * Data base inicial para geração de cobranças
+ */
+ dataBaseInicio?: Date | string
+ /**
+ * Data base final para geração de cobranças
+ */
+ dataBaseFinal?: Date | string
+ /**
+ *
+ */
+ situacao?: ISituacao
+ /**
+ * ID do contato
+ */
+ idContato?: number
+ /**
+ * ID do contato de cobrança
+ */
+ idContatoCobranca?: number
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ descricao: string
+ data: string
+ numero: string
+ valor: number
+ situacao: ISituacao
+ contato: { id: number }
+ }[]
+}
diff --git a/src/entities/contratos/interfaces/update.interface.ts b/src/entities/contratos/interfaces/update.interface.ts
new file mode 100644
index 0000000..4dff4ed
--- /dev/null
+++ b/src/entities/contratos/interfaces/update.interface.ts
@@ -0,0 +1,69 @@
+import { INotaFiscalDescontarImpostoRenda } from '../types/nota-fiscal-descontar-imposto-renda.type'
+import { INotaFiscalGerar } from '../types/nota-fiscal-gerar.type'
+import { INotaFiscalMes } from '../types/nota-fiscal-mes.type'
+import { IPeriodicidade } from '../types/periodicidade.type'
+import { ISituacao } from '../types/situacao.type'
+import { ITipoManutencao } from '../types/tipo-manutencao.type'
+import { ITipoVencimento } from '../types/tipo-vencimento.type'
+
+export interface IUpdateParams {
+ /**
+ * ID do contrato
+ */
+ idContrato: number
+}
+
+export interface IUpdateBody {
+ descricao: string
+ data: string
+ numero: string
+ valor: number
+ situacao: ISituacao
+ contato: { id: number }
+ dataFim: string
+ tipoManutencao: ITipoManutencao
+ emitirOrdemServico: boolean
+ observacoes: string
+ vendedor: {
+ id: number
+ comissao: {
+ aliquota: number
+ numeroParcelas: number
+ }
+ }
+ categoria: { id: number }
+ desconto: {
+ valor: number
+ dataFim: string
+ }
+ contaContabil: { id: number }
+ formaPagamento: { id: number }
+ notaFiscal: {
+ mes: INotaFiscalMes
+ gerar: INotaFiscalGerar
+ descontarImpostoRenda: INotaFiscalDescontarImpostoRenda
+ texto: string
+ cfop: string
+ iss: {
+ descontar: boolean
+ aliquota: number
+ }
+ item: {
+ codigoServico: string
+ produto: { id: number }
+ }
+ }
+ cobranca: {
+ dataBase: string
+ contato: { id: number }
+ vencimento: {
+ tipo: ITipoVencimento
+ dia: number
+ periodicidade: IPeriodicidade
+ }
+ }
+}
+
+export interface IUpdateResponse {
+ data: { id: number }
+}
diff --git a/src/entities/contratos/types/nota-fiscal-descontar-imposto-renda.type.ts b/src/entities/contratos/types/nota-fiscal-descontar-imposto-renda.type.ts
new file mode 100644
index 0000000..c9000f5
--- /dev/null
+++ b/src/entities/contratos/types/nota-fiscal-descontar-imposto-renda.type.ts
@@ -0,0 +1,8 @@
+/**
+ * Reter o IR e descontar do total da NFS-e caso ultrapasse R$ 10,00.
+ *
+ * - `1`: Sim
+ * - `2`: Não
+ * - `3`: Utilizar padrão da configuração da NFS-e
+ */
+export type INotaFiscalDescontarImpostoRenda = 1 | 2 | 3
diff --git a/src/entities/contratos/types/nota-fiscal-gerar.type.ts b/src/entities/contratos/types/nota-fiscal-gerar.type.ts
new file mode 100644
index 0000000..e8f0a93
--- /dev/null
+++ b/src/entities/contratos/types/nota-fiscal-gerar.type.ts
@@ -0,0 +1,7 @@
+/**
+ * Tipagem referente à geração de uma nota fiscal ao criar um contrato.
+ *
+ * - `1`: Não
+ * - `2`: Ao gerar cobrança
+ */
+export type INotaFiscalGerar = 1 | 2
diff --git a/src/entities/contratos/types/nota-fiscal-mes.type.ts b/src/entities/contratos/types/nota-fiscal-mes.type.ts
new file mode 100644
index 0000000..6e3f8f3
--- /dev/null
+++ b/src/entities/contratos/types/nota-fiscal-mes.type.ts
@@ -0,0 +1,9 @@
+/**
+ * Período de referência da cobrança que será incluído nas informações
+ * complementares das notas fiscais e observações da conta a receber.
+ *
+ * - `1`: Não imprime
+ * - `2`: Mês atual
+ * - `3`: Mês anterior
+ */
+export type INotaFiscalMes = 1 | 2 | 3
diff --git a/src/entities/contratos/types/periodicidade.type.ts b/src/entities/contratos/types/periodicidade.type.ts
new file mode 100644
index 0000000..fcac001
--- /dev/null
+++ b/src/entities/contratos/types/periodicidade.type.ts
@@ -0,0 +1,12 @@
+/**
+ * Tipagem referente à periodicidade de um contrato.
+ *
+ * - `1`: Mensal
+ * - `2`: Bimestral
+ * - `3`: Trimestral
+ * - `4`: Semestral
+ * - `5`: Anual
+ * - `6`: Bianual
+ * - `7`: Trianual
+ */
+export type IPeriodicidade = 1 | 2 | 3 | 4 | 5 | 6 | 7
diff --git a/src/entities/contratos/types/situacao.type.ts b/src/entities/contratos/types/situacao.type.ts
new file mode 100644
index 0000000..5ee1219
--- /dev/null
+++ b/src/entities/contratos/types/situacao.type.ts
@@ -0,0 +1,10 @@
+/**
+ * Tipagem referente à situação do contrato.
+ *
+ * - `0`: Inativo
+ * - `1`: Ativo
+ * - `2`: Baixado
+ * - `3`: Isento
+ * - `4`: Em avaliação
+ */
+export type ISituacao = 0 | 1 | 2 | 3 | 4
diff --git a/src/entities/contratos/types/tipo-manutencao.type.ts b/src/entities/contratos/types/tipo-manutencao.type.ts
new file mode 100644
index 0000000..65e938f
--- /dev/null
+++ b/src/entities/contratos/types/tipo-manutencao.type.ts
@@ -0,0 +1,7 @@
+/**
+ * Tipagem referente ao tipo de manutenção do contrato.
+ *
+ * - `1`: Valor
+ * - `2`: Indexação
+ */
+export type ITipoManutencao = 1 | 2
diff --git a/src/entities/contratos/types/tipo-vencimento.type.ts b/src/entities/contratos/types/tipo-vencimento.type.ts
new file mode 100644
index 0000000..c8ba2a8
--- /dev/null
+++ b/src/entities/contratos/types/tipo-vencimento.type.ts
@@ -0,0 +1,8 @@
+/**
+ * Tipagem referente ao tipo de vencimento de um contrato.
+ *
+ * - `1`: No mês corrente
+ * - `2`: No mês seguinte
+ * - `3`: Em dois meses
+ */
+export type ITipoVencimento = 1 | 2 | 3
diff --git a/src/entities/ctes.ts b/src/entities/ctes.ts
deleted file mode 100644
index 51a4556..0000000
--- a/src/entities/ctes.ts
+++ /dev/null
@@ -1,216 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import All from '../core/functions/all'
-import Find from '../core/functions/find'
-import FindBy from '../core/functions/findBy'
-import Create from '../core/functions/create'
-import Update from '../core/functions/update'
-import Delete from '../core/functions/delete'
-
-export interface ICte {
- pedido: {
- xml: string
- }
-}
-
-export interface ICteFilters {
- dataEmissao?: string
-}
-
-export type ICteInfos = Record
-
-export interface ICteDeleteResponse {
- id: string
- mensagem: string
-}
-
-export interface ICteResponse {
- id: string
- serie: string
- numero: string
- natureza: string
- cfop: string
- dataEmissao: string
- situacao: string
- tipoCte: string
- tipoServico: string
- modal: string
- municipioInicio: string
- UFInicio: string
- municipioFim: string
- UFFim: string
- cst: string
- baseCalculo: string
- aliquotaIcms: string
- valorIcms: string
- valorPis: string
- valorCofins: string
- valorIr: string
- valorInss: string
- valorCsll: string
- valorTotal: string
- rntrc: string
- emissor: {
- nome: string
- cnpj: string
- ie: string
- endereco: string
- numero: string
- complemento?: string
- bairro: string
- cep: string
- municipio: string
- uf: string
- fone: string
- }
- remetente: {
- nome: string
- cnpj: string
- ie: string
- endereco: string
- numero: string
- complemento?: string
- bairro: string
- cep: string
- municipio: string
- uf: string
- fone?: string
- email?: string
- }
- destinatario: {
- nome: string
- cnpj: string
- ie: string
- endereco: string
- numero: string
- complemento: string
- bairro: string
- cep: string
- municipio: string
- uf: string
- fone?: string
- email?: string
- }
- autorizacao: {
- chave: string
- dataRecebimento: string
- numeroProtocolo: string
- }
- nota: {
- chave: string
- }
-}
-
-export default function Ctes (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'cte',
- pluralName: 'ctes'
- }
-
- const find = async (
- numero: number | string,
- serie: number | string,
- options?: {
- params?: ICteInfos
- raw?: boolean
- }
- ) => {
- const findMethod = new Find(config)
-
- const raw = options && options.raw !== undefined ? options.raw : config.raw
-
- // @TODO: see how to reuse the code below
- if (options) {
- if (raw) {
- return await findMethod.find(`${numero}/${serie}`, {
- params: options.params,
- raw: true
- })
- } else {
- return await findMethod.find(`${numero}/${serie}`, {
- params: options.params,
- raw: false
- })
- }
- } else {
- return await findMethod.find(`${numero}/${serie}`)
- }
- }
-
- const create = async (
- data: ICte,
- options?: {
- loja?: number | string
- raw?: boolean
- }
- ) => {
- const createMethod = new Create(config)
-
- const raw = options && options.raw !== undefined ? options.raw : config.raw
-
- // @TODO: see how to reuse the code below
- if (options) {
- if (raw) {
- return await createMethod.create(data, { raw: true }, options.loja)
- } else {
- return await createMethod.create(data, { raw: false }, options.loja)
- }
- } else {
- return await createMethod.create(data, { raw: false })
- }
- }
-
- const post = async (
- id: number | string,
- options?: {
- raw?: boolean
- }
- ) => {
- const createMethod = new Create({
- ...config,
- endpoint: `/cte/lancamento/contas/${id}`
- })
-
- const raw = options && options.raw !== undefined ? options.raw : config.raw
-
- // @TODO: see how to reuse the code below
- if (raw) {
- return await createMethod.create(undefined, { raw: true })
- } else {
- return await createMethod.create(undefined, { raw: false })
- }
- }
-
- const customDelete = async (
- id: number | string,
- options?: {
- raw?: boolean
- }
- ) => {
- const deleteMethod = new Delete({
- ...config,
- endpoint: '/cte/estorno/contas'
- })
-
- const raw = options && options.raw !== undefined ? options.raw : config.raw
-
- // @TODO: see how to reuse the code below
- if (raw) {
- return await deleteMethod.delete(id, { raw: true })
- } else {
- return await deleteMethod.delete(id, { raw: false })
- }
- }
-
- return Object.assign(config, {
- all: new All().all,
- find,
- findBy: new FindBy().findBy,
- create,
- post,
- update: new Update().update,
- delete: customDelete
- })
-}
diff --git a/src/entities/customizedFields.ts b/src/entities/customizedFields.ts
deleted file mode 100644
index f52feb4..0000000
--- a/src/entities/customizedFields.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import Find from '../core/functions/find'
-
-export interface ICustomizedFieldResponse {
- id: string
- alias: string
- nome: string
- descricao: string
- situacao: boolean
- tipoCampo: string
- obrigatorio: boolean
- tamanhoMinimo?: string
- tamanhoMaximo?: string
- agrupadores: {
- agrupador: {
- id: string
- nome: string
- }
- }[]
-}
-
-export default async function CustomizedField (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'campocustomizado',
- pluralName: 'camposcustomizados'
- }
-
- const find = async (
- id: 'Produtos' | 'OrdemServico' | 'Contatos',
- options?: {
- raw?: boolean
- }
- ) => {
- const findMethod = new Find<
- ICustomizedFieldResponse,
- Record
- >(config)
-
- const raw = options && options.raw !== undefined ? options.raw : config.raw
-
- if (raw) {
- return await findMethod.find(id, { raw: true })
- } else {
- return await findMethod.find(id, { raw: false })
- }
- }
-
- return Object.assign(config, { find })
-}
diff --git a/src/entities/depositos/__tests__/create-response.ts b/src/entities/depositos/__tests__/create-response.ts
new file mode 100644
index 0000000..cb6b75c
--- /dev/null
+++ b/src/entities/depositos/__tests__/create-response.ts
@@ -0,0 +1,12 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const createRequestBody = {
+ descricao: 'Depósito Geral',
+ situacao: 1 as const,
+ padrao: false,
+ desconsiderarSaldo: false
+}
diff --git a/src/entities/depositos/__tests__/find-response.ts b/src/entities/depositos/__tests__/find-response.ts
new file mode 100644
index 0000000..b0c2878
--- /dev/null
+++ b/src/entities/depositos/__tests__/find-response.ts
@@ -0,0 +1,9 @@
+export default {
+ data: {
+ id: 12345678,
+ descricao: 'Depósito Geral',
+ situacao: 1 as const,
+ padrao: false,
+ desconsiderarSaldo: false
+ }
+}
diff --git a/src/entities/depositos/__tests__/get-response.ts b/src/entities/depositos/__tests__/get-response.ts
new file mode 100644
index 0000000..e72bf1a
--- /dev/null
+++ b/src/entities/depositos/__tests__/get-response.ts
@@ -0,0 +1,11 @@
+export default {
+ data: [
+ {
+ id: 12345678,
+ descricao: 'Depósito Geral',
+ situacao: 1 as const,
+ padrao: false,
+ desconsiderarSaldo: false
+ }
+ ]
+}
diff --git a/src/entities/depositos/__tests__/index.spec.ts b/src/entities/depositos/__tests__/index.spec.ts
new file mode 100644
index 0000000..76a6445
--- /dev/null
+++ b/src/entities/depositos/__tests__/index.spec.ts
@@ -0,0 +1,86 @@
+import { Chance } from 'chance'
+import { Depositos } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import createResponse, { createRequestBody } from './create-response'
+import findResponse from './find-response'
+import getResponse from './get-response'
+import updateResponse, { updateRequestBody } from './update-response'
+
+const chance = Chance()
+
+describe('Depósitos entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: Depositos
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new Depositos(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should get successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(getResponse)
+
+ const response = await entity.get()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'depositos',
+ params: {
+ limite: undefined,
+ pagina: undefined,
+ descricao: undefined,
+ situacao: undefined
+ }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should find successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idDeposito = chance.natural()
+ repository.setResponse(findResponse)
+
+ const response = await entity.find({ idDeposito })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'depositos',
+ id: String(idDeposito)
+ })
+ expect(response).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: 'depositos',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idDeposito = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idDeposito,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'depositos',
+ id: String(idDeposito),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/depositos/__tests__/update-response.ts b/src/entities/depositos/__tests__/update-response.ts
new file mode 100644
index 0000000..d513f2a
--- /dev/null
+++ b/src/entities/depositos/__tests__/update-response.ts
@@ -0,0 +1,27 @@
+export default {
+ id: 12345678,
+ alertas: [
+ {
+ 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'
+ }
+ ]
+ }
+ ]
+}
+
+export const updateRequestBody = {
+ descricao: 'Depósito Geral',
+ situacao: 1 as const,
+ padrao: false,
+ desconsiderarSaldo: false
+}
diff --git a/src/entities/depositos/index.ts b/src/entities/depositos/index.ts
new file mode 100644
index 0000000..13df886
--- /dev/null
+++ b/src/entities/depositos/index.ts
@@ -0,0 +1,94 @@
+import { Entity } from '../@shared/entity'
+import { ICreateBody, ICreateResponse } from './interfaces/create.interface'
+import { IFindParams, IFindResponse } from './interfaces/find.interface'
+import { IGetParams, IGetResponse } from './interfaces/get.interface'
+import {
+ IUpdateBody,
+ IUpdateParams,
+ IUpdateResponse
+} from './interfaces/update.interface'
+
+/**
+ * Entidade para interação com depósitos.
+ *
+ * @see https://developer.bling.com.br/referencia#/Dep%C3%B3sitos
+ */
+export class Depositos extends Entity {
+ /**
+ * Obtém depósitos.
+ *
+ * @param {IGetParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Dep%C3%B3sitos/get_depositos
+ */
+ public async get(params?: IGetParams): Promise {
+ return await this.repository.index({
+ endpoint: 'depositos',
+ params: {
+ pagina: params?.pagina,
+ limite: params?.limite,
+ descricao: params?.descricao,
+ situacao: params?.situacao
+ }
+ })
+ }
+
+ /**
+ * Obtém um depósito.
+ *
+ * @param {IFindParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Dep%C3%B3sitos/get_depositos__idDeposito_
+ */
+ public async find(params: IFindParams): Promise {
+ return await this.repository.show({
+ endpoint: 'depositos',
+ id: String(params.idDeposito)
+ })
+ }
+
+ /**
+ * Cria um depósito.
+ *
+ * @param {ICreateBody} body O conteúdo para a criação.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Dep%C3%B3sitos/post_depositos
+ */
+ public async create(body: ICreateBody): Promise {
+ return await this.repository.store({
+ endpoint: 'depositos',
+ body
+ })
+ }
+
+ /**
+ * Altera um depósito.
+ *
+ * @param {IUpdateParams & IUpdateBody} params Os parâmetros da atualização.
+ *
+ * @return {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Dep%C3%B3sitos/put_depositos__idDeposito_
+ */
+ public async update(
+ params: IUpdateParams & IUpdateBody
+ ): Promise {
+ const { idDeposito, ...body } = params
+
+ return await this.repository.replace({
+ endpoint: 'depositos',
+ id: String(idDeposito),
+ body
+ })
+ }
+}
diff --git a/src/entities/depositos/interfaces/create.interface.ts b/src/entities/depositos/interfaces/create.interface.ts
new file mode 100644
index 0000000..0ac517d
--- /dev/null
+++ b/src/entities/depositos/interfaces/create.interface.ts
@@ -0,0 +1,12 @@
+import ISituacao from 'src/entities/@shared/types/situacao.type'
+
+export interface ICreateBody {
+ descricao: string
+ situacao: ISituacao
+ padrao: boolean
+ desconsiderarSaldo: boolean
+}
+
+export interface ICreateResponse {
+ data: { id: number }
+}
diff --git a/src/entities/depositos/interfaces/find.interface.ts b/src/entities/depositos/interfaces/find.interface.ts
new file mode 100644
index 0000000..240ba66
--- /dev/null
+++ b/src/entities/depositos/interfaces/find.interface.ts
@@ -0,0 +1,18 @@
+import ISituacao from 'src/entities/@shared/types/situacao.type'
+
+export interface IFindParams {
+ /**
+ * ID do depósito
+ */
+ idDeposito: number
+}
+
+export interface IFindResponse {
+ data: {
+ id: number
+ descricao: string
+ situacao: ISituacao
+ padrao: boolean
+ desconsiderarSaldo: boolean
+ }
+}
diff --git a/src/entities/depositos/interfaces/get.interface.ts b/src/entities/depositos/interfaces/get.interface.ts
new file mode 100644
index 0000000..17cb4a4
--- /dev/null
+++ b/src/entities/depositos/interfaces/get.interface.ts
@@ -0,0 +1,30 @@
+import ISituacao from 'src/entities/@shared/types/situacao.type'
+
+export interface IGetParams {
+ /**
+ * N° da página da listagem
+ */
+ pagina?: number
+ /**
+ * Quantidade de registros que devem ser exibidos por página
+ */
+ limite?: number
+ /**
+ * Descrição do depósito
+ */
+ descricao?: string
+ /**
+ *
+ */
+ situacao?: ISituacao
+}
+
+export interface IGetResponse {
+ data: {
+ id: number
+ descricao: string
+ situacao: ISituacao
+ padrao: boolean
+ desconsiderarSaldo: boolean
+ }[]
+}
diff --git a/src/entities/depositos/interfaces/update.interface.ts b/src/entities/depositos/interfaces/update.interface.ts
new file mode 100644
index 0000000..31c3a0a
--- /dev/null
+++ b/src/entities/depositos/interfaces/update.interface.ts
@@ -0,0 +1,21 @@
+import { IDefaultErrorFieldsResponse } from 'src/entities/@shared/interfaces/error.interface'
+import ISituacao from 'src/entities/@shared/types/situacao.type'
+
+export interface IUpdateParams {
+ /**
+ * ID do depósito
+ */
+ idDeposito: number
+}
+
+export interface IUpdateBody {
+ descricao: string
+ situacao: ISituacao
+ padrao: boolean
+ desconsiderarSaldo: boolean
+}
+
+export interface IUpdateResponse {
+ id: number
+ alertas?: IDefaultErrorFieldsResponse[]
+}
diff --git a/src/entities/deposits.ts b/src/entities/deposits.ts
deleted file mode 100644
index 0a49a87..0000000
--- a/src/entities/deposits.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { IApiInstance } from '../core/interfaces/method'
-
-import All from '../core/functions/all'
-import Find from '../core/functions/find'
-import FindBy from '../core/functions/findBy'
-import Create from '../core/functions/create'
-import Update from '../core/functions/update'
-
-export interface IDeposit {
- descricao?: string
- desconsiderarSaldo?: boolean
- depositoPadrao?: boolean
- situacao?: 'A' | 'I'
-}
-
-export interface IDepositFilters {
- situacao?: 'A' | 'I'
-}
-
-export type IDepositInfos = Record
-
-export interface IDepositResponse {
- id: string
- descricao: string
- situacao: string
- depositaoPadrao: string
- desconsiderarSaldo: 'true' | 'false'
-}
-
-export default function Deposits (api: IApiInstance, raw: boolean) {
- const config = {
- api,
- raw,
- singularName: 'deposito',
- pluralName: 'depositos'
- }
-
- return Object.assign(config, {
- all: new All().all,
- find: new Find().find,
- findBy: new FindBy()
- .findBy,
- create: new Create().create,
- update: new Update().update
- })
-}
diff --git a/src/entities/empresas/__tests__/index.spec.ts b/src/entities/empresas/__tests__/index.spec.ts
new file mode 100644
index 0000000..f1f9b85
--- /dev/null
+++ b/src/entities/empresas/__tests__/index.spec.ts
@@ -0,0 +1,29 @@
+import { Empresas } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import meResponse from './me-response'
+
+describe('Empresas entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: Empresas
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new Empresas(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should get successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ repository.setResponse(meResponse)
+
+ const response = await entity.me()
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'empresas/me/dados-basicos'
+ })
+ expect(response).toBe(meResponse)
+ })
+})
diff --git a/src/entities/empresas/__tests__/me-response.ts b/src/entities/empresas/__tests__/me-response.ts
new file mode 100644
index 0000000..6eb0d96
--- /dev/null
+++ b/src/entities/empresas/__tests__/me-response.ts
@@ -0,0 +1,7 @@
+export default {
+ data: {
+ nome: 'Empresa Teste LTDA',
+ cnpj: '12.345.657/8910-11',
+ email: 'empresa@email.com'
+ }
+}
diff --git a/src/entities/empresas/index.ts b/src/entities/empresas/index.ts
new file mode 100644
index 0000000..badd225
--- /dev/null
+++ b/src/entities/empresas/index.ts
@@ -0,0 +1,23 @@
+import { Entity } from '../@shared/entity'
+import { IMeResponse } from './interfaces/me.interface'
+
+/**
+ * Entidade para interação com empresas.
+ *
+ * @see https://developer.bling.com.br/referencia#/Empresas
+ */
+export class Empresas extends Entity {
+ /**
+ * Obtém dados básicos da empresa.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Empresas/get_empresas_me_dados_basicos
+ */
+ public async me(): Promise {
+ return await this.repository.index({
+ endpoint: 'empresas/me/dados-basicos'
+ })
+ }
+}
diff --git a/src/entities/empresas/interfaces/me.interface.ts b/src/entities/empresas/interfaces/me.interface.ts
new file mode 100644
index 0000000..368d5f8
--- /dev/null
+++ b/src/entities/empresas/interfaces/me.interface.ts
@@ -0,0 +1,7 @@
+export interface IMeResponse {
+ data: {
+ nome: string
+ cnpj: string
+ email: string
+ }
+}
diff --git a/src/entities/estoques/__tests__/create-response.ts b/src/entities/estoques/__tests__/create-response.ts
new file mode 100644
index 0000000..48961fa
--- /dev/null
+++ b/src/entities/estoques/__tests__/create-response.ts
@@ -0,0 +1,19 @@
+export default {
+ data: {
+ id: 12345678
+ }
+}
+
+export const createRequestBody = {
+ produto: {
+ id: 12345678
+ },
+ deposito: {
+ id: 12345678
+ },
+ operacao: 'B' as const,
+ preco: 1500.75,
+ custo: 1500.75,
+ quantidade: 50.75,
+ observacoes: 'Observações de estoque'
+}
diff --git a/src/entities/estoques/__tests__/find-balance-response.ts b/src/entities/estoques/__tests__/find-balance-response.ts
new file mode 100644
index 0000000..8c1f29e
--- /dev/null
+++ b/src/entities/estoques/__tests__/find-balance-response.ts
@@ -0,0 +1,11 @@
+export default {
+ data: [
+ {
+ produto: {
+ id: 12345678
+ },
+ saldoFisicoTotal: 1500.75,
+ saldoVirtualTotal: 1500.75
+ }
+ ]
+}
diff --git a/src/entities/estoques/__tests__/get-balances-response.ts b/src/entities/estoques/__tests__/get-balances-response.ts
new file mode 100644
index 0000000..2cbd464
--- /dev/null
+++ b/src/entities/estoques/__tests__/get-balances-response.ts
@@ -0,0 +1,18 @@
+export default {
+ data: [
+ {
+ produto: {
+ id: 12345678
+ },
+ saldoFisicoTotal: 1500.75,
+ saldoVirtualTotal: 1500.75,
+ depositos: [
+ {
+ id: 12345678,
+ saldoFisico: 1250.75,
+ saldoVirtual: 1250.75
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/entities/estoques/__tests__/index.spec.ts b/src/entities/estoques/__tests__/index.spec.ts
new file mode 100644
index 0000000..07b4858
--- /dev/null
+++ b/src/entities/estoques/__tests__/index.spec.ts
@@ -0,0 +1,90 @@
+import { Chance } from 'chance'
+import { Estoques } from '..'
+import { InMemoryBlingRepository } from '../../../repositories/bling-in-memory.repository'
+import createResponse, { createRequestBody } from './create-response'
+import findResponse from './find-balance-response'
+import getResponse from './get-balances-response'
+import updateResponse, { updateRequestBody } from './update-response'
+
+const chance = Chance()
+
+describe('Estoques entity', () => {
+ let repository: InMemoryBlingRepository
+ let entity: Estoques
+
+ beforeEach(() => {
+ repository = new InMemoryBlingRepository()
+ entity = new Estoques(repository)
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ })
+
+ it('should find balance successfully', async () => {
+ const spy = jest.spyOn(repository, 'show')
+ const idDeposito = chance.natural()
+ const idsProdutos: number[] = []
+ for (let i = 0; i < chance.natural({ min: 2, max: 5 }); i++) {
+ idsProdutos.push(chance.natural())
+ }
+ repository.setResponse(findResponse)
+
+ const response = await entity.findBalance({ idDeposito, idsProdutos })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'estoques/saldos',
+ id: String(idDeposito),
+ params: { idsProdutos }
+ })
+ expect(response).toBe(findResponse)
+ })
+
+ it('should get balances successfully', async () => {
+ const spy = jest.spyOn(repository, 'index')
+ const idsProdutos: number[] = []
+ for (let i = 0; i < chance.natural({ min: 2, max: 5 }); i++) {
+ idsProdutos.push(chance.natural())
+ }
+ repository.setResponse(getResponse)
+
+ const response = await entity.getBalances({ idsProdutos })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'estoques/saldos',
+ params: { idsProdutos }
+ })
+ expect(response).toBe(getResponse)
+ })
+
+ it('should create successfully', async () => {
+ const spy = jest.spyOn(repository, 'store')
+ repository.setResponse(createResponse)
+
+ const response = await entity.create(createRequestBody)
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'estoques',
+ body: createRequestBody
+ })
+ expect(response).toBe(createResponse)
+ })
+
+ it('should update successfully', async () => {
+ const spy = jest.spyOn(repository, 'replace')
+ const idEstoque = chance.natural()
+ repository.setResponse(updateResponse)
+
+ const response = await entity.update({
+ idEstoque,
+ ...updateRequestBody
+ })
+
+ expect(spy).toHaveBeenCalledWith({
+ endpoint: 'estoques',
+ id: String(idEstoque),
+ body: updateRequestBody
+ })
+ expect(response).toBe(updateResponse)
+ })
+})
diff --git a/src/entities/estoques/__tests__/update-response.ts b/src/entities/estoques/__tests__/update-response.ts
new file mode 100644
index 0000000..c0d79be
--- /dev/null
+++ b/src/entities/estoques/__tests__/update-response.ts
@@ -0,0 +1,10 @@
+export default null
+
+export const updateRequestBody = {
+ operacao: 'B' as const,
+ preco: 1500.75,
+ custo: 1500.75,
+ quantidade: 50.75,
+ observacoes: 'Observações de estoque',
+ data: '2023-02-10 11:41:27'
+}
diff --git a/src/entities/estoques/index.ts b/src/entities/estoques/index.ts
new file mode 100644
index 0000000..d734aa5
--- /dev/null
+++ b/src/entities/estoques/index.ts
@@ -0,0 +1,94 @@
+import { Entity } from '../@shared/entity'
+import { ICreateBody, ICreateResponse } from './interfaces/create.interface'
+import {
+ IFindBalanceParams,
+ IFindBalanceResponse
+} from './interfaces/find-balance.interface'
+import {
+ IGetBalancesParams,
+ IGetBalancesResponse
+} from './interfaces/get-balances.interface'
+import { IUpdateBody, IUpdateParams } from './interfaces/update.interface'
+
+/**
+ * Entidade para interação com estoques.
+ *
+ * @see https://developer.bling.com.br/referencia#/Estoques
+ */
+export class Estoques extends Entity {
+ /**
+ * Obtém o saldo em estoque de produtos por depósito.
+ *
+ * @param {IFindBalanceParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Estoques/get_estoques_saldos__idDeposito_
+ */
+ public async findBalance(
+ params: IFindBalanceParams
+ ): Promise {
+ return await this.repository.show({
+ endpoint: 'estoques/saldos',
+ id: String(params.idDeposito),
+ params: { idsProdutos: params.idsProdutos }
+ })
+ }
+
+ /**
+ * Obtém o saldo em estoque de produtos.
+ *
+ * @param {IGetBalancesParams} params Parâmetros da busca.
+ *
+ * @returns {Promise}
+ * @throws {BlingApiException|BlingInternalException}
+ *
+ * @see https://developer.bling.com.br/referencia#/Estoques/get_estoques_saldos
+ */
+ public async getBalances(
+ params: IGetBalancesParams
+ ): Promise {
+ return await this.repository.index({
+ endpoint: 'estoques/saldos',
+ params: { idsProdutos: params.idsProdutos }
+ })
+ }
+
+ /**
+ * Cria um registro de estoque.
+ *
+ * @param {ICreateBody} body O conteúdo para a criação.
+ *
+ * @returns {Promise