diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml
index d4e3dd37b..5f74702ed 100644
--- a/.github/workflows/cd.yaml
+++ b/.github/workflows/cd.yaml
@@ -3,7 +3,7 @@ name: Deploy [prod]
on:
push:
branches:
- - "main"
+ - "master"
paths-ignore:
- 'README.md'
- 'docs/**'
diff --git a/README.md b/README.md
index e50ec456c..e1ad3707e 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ Login no adminer (login de exemplo):
* Senha: `secret`
* Base de dados: `api`
-Configurar projeto:
+## Configurar projeto:
```bash
npm install
@@ -80,6 +80,21 @@ npm run seed:run
npm run start:dev
```
+Este projeto depende de algumas tabelas preenchidas. Por isso, execute os scripts abaixo para
+preencher as necessárias:
+
+- local_dev_example/sql/carga_settings.sql
+- local_dev_example/sql/carga_users.sql
+- local_dev_example/sql/carga_pagador.sql
+
+O SGBD PostgreSQL também necessita ter uma extensão para executar:
+```sql
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
+```
+## Swagger
+A documentação swagger é acessível pelo endereço: http://localhost:3001/docs
+
+
Rodar seed caso o banco não esteja vazio:
```bash
diff --git a/docs/NovoRemessa/Nova Arquitetura Remessa.drawio b/docs/NovoRemessa/Nova Arquitetura Remessa.drawio
new file mode 100644
index 000000000..541247bb3
--- /dev/null
+++ b/docs/NovoRemessa/Nova Arquitetura Remessa.drawio
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/local_dev_example/postgresql/docker-compose.yml b/local_dev_example/postgresql/docker-compose.yml
new file mode 100644
index 000000000..7a666fdc6
--- /dev/null
+++ b/local_dev_example/postgresql/docker-compose.yml
@@ -0,0 +1,15 @@
+version: '3.9'
+
+services:
+ postgres:
+ image: postgres:17.2
+ ports:
+ - 5432:5432
+ volumes:
+ - pg-data:/var/lib/postgresql/data
+ environment:
+ - POSTGRES_PASSWORD=admin
+ - POSTGRES_USER=admin
+ - POSTGRES_DB=admin
+volumes:
+ pg-data:
\ No newline at end of file
diff --git a/local_dev_example/sftp/docker-compose.yml b/local_dev_example/sftp/docker-compose.yml
new file mode 100644
index 000000000..d1d9e76d6
--- /dev/null
+++ b/local_dev_example/sftp/docker-compose.yml
@@ -0,0 +1,8 @@
+version: '3'
+services:
+ sftp:
+ image: "emberstack/sftp"
+ ports:
+ - "23:22"
+ volumes:
+ - ../config/sftp.json:/app/config/sftp.json:ro
\ No newline at end of file
diff --git a/local_dev_example/sql/carga_pagador.sql b/local_dev_example/sql/carga_pagador.sql
new file mode 100644
index 000000000..a4229ba8a
--- /dev/null
+++ b/local_dev_example/sql/carga_pagador.sql
@@ -0,0 +1,5 @@
+INSERT INTO public.pagador (id, "nomeEmpresa", agencia, "dvAgencia", conta, "dvConta", logradouro, numero, complemento, bairro, cidade, cep, "complementoCep", uf, "cpfCnpj") VALUES (1, 'CONTA BILHETAGEM – CB', '4064', '9', '000600071084', '8', 'R DONA MARIANA', '00048', 'ANDAR 7', 'CENTRO', 'Rio de Janeiro', '22280', '020', 'RJ', '546037000110');
+INSERT INTO public.pagador (id, "nomeEmpresa", agencia, "dvAgencia", conta, "dvConta", logradouro, numero, complemento, bairro, cidade, cep, "complementoCep", uf, "cpfCnpj") VALUES (2, 'CETT CTA ESTAB TARIFARIA TRANSP', '4064', '9', '000600071083', '0', 'R DONA MARIANA', '00048', 'ANDAR 7', 'CENTRO', 'Rio de Janeiro', '22280', '020', 'RJ', '546037000110');
+
+
+SELECT pg_catalog.setval('public.pagador_id_seq', 2, true);
\ No newline at end of file
diff --git a/local_dev_example/sql/carga_settings.sql b/local_dev_example/sql/carga_settings.sql
new file mode 100644
index 000000000..141b1294a
--- /dev/null
+++ b/local_dev_example/sql/carga_settings.sql
@@ -0,0 +1,51 @@
+-- Setting types
+INSERT INTO public.setting_type (id, name)
+VALUES (1, 'boolean');
+INSERT INTO public.setting_type (id, name)
+VALUES (2, 'string');
+INSERT INTO public.setting_type (id, name)
+VALUES (3, 'number');
+INSERT INTO public.setting_type (id, name)
+VALUES (4, 'json');
+
+
+-- Settings for local environment
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (2, 'auto_send_invite_schedule_hours', '25', NULL, false, 3, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (1, 'activate_auto_send_invite', 'true', NULL, true, 1, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (3, 'poll_db_enabled', 'true', NULL, false, 1, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (4, 'poll_db_cronjob', '*/1 * * * *', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (5, 'mail_invite_cronjob', '0 22 * * *', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (7, 'mail_report_enabled', 'true', NULL, false, 1, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (10, 'ab_test_enabled', 'false', '1', false, 1, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (11, 'user_file_max_upload_size', '10MB', '1', false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (9, 'mail_report_recipient_2', 'laurosilvestre.smtr@gmail.com', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (13, 'bigquery_env', 'production', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (17, 'mail_report_recipient_3', 'felipelins.smtr@gmail.com', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (14, 'cnab_current_nsr_date', '2024-06-28T04:00:01.983Z', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (18, 'cnab_last_nsr_sequence', '0', NULL, false, 3, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (15, 'cnab_current_nsr_sequence', '0', NULL, false, 3, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (19, 'cnab_jobs_enabled', 'true', NULL, false, 1, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (6, 'mail_report_cronjob', '0 9 * * *', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (16, 'api_env', 'local', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (8, 'mail_report_recipient_1', 'marcosbernardo@prefeitura.rio', NULL, false, 2, '2024-09-23 19:22:08.703643');
+INSERT INTO public.setting (id, name, value, version, editable, "settingTypeId", "updatedAt")
+VALUES (12, 'cnab_current_nsa', '568', NULL, false, 3, '2024-12-19 15:37:24.713758');
+
diff --git a/local_dev_example/sql/carga_users.sql b/local_dev_example/sql/carga_users.sql
new file mode 100644
index 000000000..8894c62b7
--- /dev/null
+++ b/local_dev_example/sql/carga_users.sql
@@ -0,0 +1,56 @@
+INSERT INTO public.role (id, name)
+VALUES (2, 'User');
+INSERT INTO public.role (id, name)
+VALUES (1, 'Admin');
+INSERT INTO public.role (id, name)
+VALUES (0, 'Admin Master');
+INSERT INTO public.role (id, name)
+VALUES (3, 'Lançador financeiro');
+INSERT INTO public.role (id, name)
+VALUES (4, 'Aprovador financeiro');
+INSERT INTO public.role (id, name)
+VALUES (5, 'Admin Finan');
+
+-- password 123456
+INSERT INTO public."user"(id, email, password, provider, "socialId", "fullName", "firstName", "lastName", hash,
+ "createdAt", "updatedAt", "deletedAt", "roleId", "statusId", "permitCode", "cpfCnpj",
+ "bankCode", "bankAgency", "bankAccount", "bankAccountDigit", phone, "isSgtuBlocked", "passValidatorId")
+VALUES
+ (1, 'admin.test@prefeitura.rio', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'Test User', 'Test', 'User', null, now(), now(), null, 1, null, '123456', '9363945131', 001, '0001', '12345678', '9', '5551999999999', false, null),
+ (2, 'user1@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'John Doe', 'John', 'Doe', null, now(), now(), null, 1, null, '654321', '12345678900', 237, '0002', '87654321', '8', '5551888888888', false, null),
+ (3, 'user2@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'Jane Smith', 'Jane', 'Smith', null, now(), now(), null, 1, null, '987654', '98765432100', 104, '0003', '11223344', '7', '5551777777777', false, null),
+ (4, 'user3@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'Alice Brown', 'Alice', 'Brown', null, now(), now(), null, 1, null, '456789', '12312312399', 341, '0004', '55667788', '6', '5551666666666', false, null),
+ (5, 'user4@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'Bob White', 'Bob', 'White', null, now(), now(), null, 1, null, '789123', '98798798788', 033, '0005', '99887766', '5', '5551555555555', false, null),
+ (6, 'user5@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'Charlie Green', 'Charlie', 'Green', null, now(), now(), null, 1, null, '321654', '11122233344', 077, '0006', '12344321', '4', '5551444444444', false, null),
+ (7, 'user6@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'David Black', 'David', 'Black', null, now(), now(), null, 1, null, '654987', '55566677788', 748, '0007', '44556677', '3', '5551333333333', false, null),
+ (8, 'user7@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'Emma Wilson', 'Emma', 'Wilson', null, now(), now(), null, 1, null, '789456', '99988877766', 212, '0008', '66778899', '2', '5551222222222', false, null),
+ (9, 'user8@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'Frank Brown', 'Frank', 'Brown', null, now(), now(), null, 1, null, '123789', '33344455599', 399, '0009', '88990011', '1', '5551111111111', false, null),
+ (10, 'user9@example.com', '$2b$12$Fj8WzjBY1fEiPjV42SbtpOKNtr9vgKXQpfpW784Vo7Znh7qVIIpRy', 'email', null, 'Grace Taylor', 'Grace', 'Taylor', null, now(), now(), null, 1, null, '987321', '11133355577', 001, '0010', '11122233', '0', '5551000000000', false, null);
+
+
+-- atualiza o permit code dos consorcios
+update "user"
+set "permitCode" = (select "idConsorcio" from ordem_pagamento where "nomeConsorcio" = 'VLT' limit 1)
+where "cpfCnpj" = '18201378000119'; -- permit code = id consorcio
+
+update "user"
+set "permitCode" = (select "idConsorcio" from ordem_pagamento where "nomeConsorcio" = 'Intersul' limit 1)
+where "cpfCnpj" = '12464869000176'; -- permit code = id consorcio
+
+
+update "user"
+set "permitCode" = (select "idConsorcio" from ordem_pagamento where "nomeConsorcio" = 'Internorte' limit 1)
+where "cpfCnpj" = '12464539000180'; -- permit code = id consorcio
+
+update "user"
+set "permitCode" = (select "idConsorcio" from ordem_pagamento where "nomeConsorcio" = 'Transcarioca' limit 1)
+where "cpfCnpj" = '12464553000184';
+
+
+update "user"
+set "permitCode" = (select "idConsorcio" from ordem_pagamento where "nomeConsorcio" = 'MobiRio' limit 1)
+where "cpfCnpj" = '44520687000161';
+
+update "user"
+set "permitCode" = (select "idConsorcio" from ordem_pagamento where "nomeConsorcio" = 'Santa Cruz' limit 1)
+where "cpfCnpj" = '12464577000133';
\ No newline at end of file
diff --git a/local_dev_example/sql/carga_users_ordem_pagamento.sql b/local_dev_example/sql/carga_users_ordem_pagamento.sql
new file mode 100644
index 000000000..8d9d78593
--- /dev/null
+++ b/local_dev_example/sql/carga_users_ordem_pagamento.sql
@@ -0,0 +1,22 @@
+-- Distribui ordens de pagamentos entre os usuários com ID 1 a 10 para fins de teste.
+DO $$
+ DECLARE
+ ordens RECORD;
+ user_id INTEGER;
+ BEGIN
+ -- Loop para percorrer cada ordem de pagamento com userId nulo
+ FOR ordens IN (
+ SELECT id
+ FROM public.ordem_pagamento
+ WHERE "userId" IS NULL
+ )
+ LOOP
+ -- Gerar um userId aleatório entre 1 e 10
+ user_id := (FLOOR(random() * 10) + 1)::INTEGER;
+
+ -- Atualizar o registro com o userId gerado
+ UPDATE public.ordem_pagamento
+ SET "userId" = user_id
+ WHERE id = ordens.id;
+ END LOOP;
+ END $$;
diff --git a/package-lock.json b/package-lock.json
index 33a95194c..70836dcac 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "api-cct",
- "version": "0.11.0",
+ "version": "0.13.13",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "api-cct",
- "version": "0.11.0",
+ "version": "0.13.13",
"license": "UNLICENSED",
"dependencies": {
"@aws-sdk/client-s3": "3.350.0",
diff --git a/src/bigquery/bigquery.service.ts b/src/bigquery/bigquery.service.ts
index 5f30815aa..b6892279f 100644
--- a/src/bigquery/bigquery.service.ts
+++ b/src/bigquery/bigquery.service.ts
@@ -97,12 +97,13 @@ export class BigqueryService {
* Run bigquery query with complete log and error handling
* @throws `HttpException`
*/
- public async query(source: BigquerySource, query: string) {
+ public async query(source: BigquerySource, query: string, params?: any[]) {
const _query = compactQuery(query);
console.log(`${formatSqlTitle('bigquery:')} ${formatSqlQuery(query)}`);
try {
const [rows] = await this.getBqInstance(source).query({
query: _query,
+ params: params,
});
this.logger.debug(`Query finished. Count: ${rows.length}`);
return rows;
diff --git a/src/bigquery/dtos/bigquery-ordem-pagamento.dto.ts b/src/bigquery/dtos/bigquery-ordem-pagamento.dto.ts
index 35434693f..85d2df1c7 100644
--- a/src/bigquery/dtos/bigquery-ordem-pagamento.dto.ts
+++ b/src/bigquery/dtos/bigquery-ordem-pagamento.dto.ts
@@ -15,10 +15,16 @@ export class BigqueryOrdemPagamentoDTO {
}
}
+ /** id_ordem_pagamento_consorcio_operador_dia */
+ @IsNotEmpty()
+ id: number;
+
/**
* Data da ordem de pagamento (partição)
*
* Para filtrar e ordenar por data
+ *
+ * @example `2024-12-25`
*/
@IsNotEmpty()
@IsDateString()
@@ -151,6 +157,10 @@ export class BigqueryOrdemPagamentoDTO {
tipoFavorecido: TipoFavorecidoEnum | null;
+ datetimeUltimaAtualizacao: Date;
+
+ dataCaptura: Date;
+
public static findAgrupado(ordemAgs: BigqueryOrdemPagamentoDTO[], ordem: BigqueryOrdemPagamentoDTO, newDataOrdem = nextFriday(new Date())) {
const filtered = ordemAgs.filter((i) => isSameDay(new Date(i.dataOrdem), newDataOrdem) && i.idConsorcio === ordem.idConsorcio)[0];
return filtered ? filtered : null;
diff --git a/src/bigquery/entities/ordem-pagamento.bigquery-entity.ts b/src/bigquery/entities/ordem-pagamento.bigquery-entity.ts
index d0c1041a7..7aa838f8b 100644
--- a/src/bigquery/entities/ordem-pagamento.bigquery-entity.ts
+++ b/src/bigquery/entities/ordem-pagamento.bigquery-entity.ts
@@ -84,6 +84,9 @@ export class BigqueryOrdemPagamento {
/** Código de controle de versão do dado (SHA Github) */
versao: string;
+ /** Data de captura **/
+ dataCaptura: Date;
+
public static getGroupId(ordem: BigqueryOrdemPagamento) {
return `${ordem.idOrdemPagamento},${ordem.idConsorcio},${ordem.idOperadora}`;
}
diff --git a/src/bigquery/entities/transacao.bigquery-entity.ts b/src/bigquery/entities/transacao.bigquery-entity.ts
index 8d2f4a047..148b91b09 100644
--- a/src/bigquery/entities/transacao.bigquery-entity.ts
+++ b/src/bigquery/entities/transacao.bigquery-entity.ts
@@ -23,6 +23,7 @@ export class BigqueryTransacao {
id_transacao: string;
tipo_pagamento: string;
tipo_transacao: string | null;
+ tipo_transacao_smtr: string | null;
tipo_gratuidade: string | null;
tipo_integracao: string | null;
id_integracao: number | null;
diff --git a/src/bigquery/interfaces/bigquery-find-ordem-pagamento.interface.ts b/src/bigquery/interfaces/bigquery-find-ordem-pagamento.interface.ts
index c168c6cea..dfc5e816c 100644
--- a/src/bigquery/interfaces/bigquery-find-ordem-pagamento.interface.ts
+++ b/src/bigquery/interfaces/bigquery-find-ordem-pagamento.interface.ts
@@ -3,6 +3,7 @@ import { TipoFavorecidoEnum } from 'src/cnab/enums/tipo-favorecido.enum';
export interface IBigqueryFindOrdemPagamento {
operadorCpfs?: string[];
tipoFavorecido?: TipoFavorecidoEnum | null;
+ consorcioName?: string[];
startDate: Date;
endDate: Date;
limit?: number;
diff --git a/src/bigquery/repositories/bigquery-ordem-pagamento.repository.ts b/src/bigquery/repositories/bigquery-ordem-pagamento.repository.ts
index 1f9ad5449..23bb7c9b3 100644
--- a/src/bigquery/repositories/bigquery-ordem-pagamento.repository.ts
+++ b/src/bigquery/repositories/bigquery-ordem-pagamento.repository.ts
@@ -86,10 +86,11 @@ export class BigqueryOrdemPagamentoRepository {
SELECT
CAST(t.data_ordem AS STRING) AS dataOrdem,
CAST(t.data_pagamento AS STRING) AS dataPagamento,
+ t.id_ordem_pagamento_consorcio_operador_dia AS id,
t.id_consorcio AS idConsorcio,
t.consorcio,
t.id_operadora AS idOperadora,
- t.operadora AS operadora,
+ o.operadora_completo AS operadora,
t.id_ordem_pagamento AS idOrdemPagamento,
t.quantidade_transacao_debito AS quantidadeTransacaoDebito,
t.valor_debito AS valorDebito,
@@ -110,6 +111,8 @@ export class BigqueryOrdemPagamentoRepository {
o.tipo_documento AS operadoraTipoDocumento,
CAST(c.cnpj AS STRING) AS consorcioCnpj,
CAST(o.documento AS STRING) AS operadoraCpfCnpj,
+ CAST(datetime_ultima_atualizacao AS STRING) AS datetimeUltimaAtualizacao,
+ CAST(t.datetime_captura AS STRING) dataCaptura
FROM \`rj-smtr.br_rj_riodejaneiro_bilhetagem.ordem_pagamento_consorcio_operador_dia\` t
LEFT JOIN \`rj-smtr.cadastro.operadoras\` o ON o.id_operadora = t.id_operadora
LEFT JOIN \`rj-smtr.cadastro.consorcios\` c ON c.id_consorcio = t.id_consorcio \n
@@ -137,18 +140,24 @@ export class BigqueryOrdemPagamentoRepository {
private getQueryArgsConsorcio(args: IBigqueryFindOrdemPagamento) {
const startDate = args.startDate.toISOString().slice(0, 10);
const endDate = args.endDate.toISOString().slice(0, 10);
- const qWhere =
- `t.data_ordem BETWEEN '${startDate}' AND '${endDate}' AND o.tipo_documento = 'CNPJ' ` +
+ let qWhere =
+ `date(t.datetime_captura) BETWEEN '${startDate}' AND '${endDate}' AND o.tipo_documento = 'CNPJ' ` +
'AND t.valor_total_transacao_liquido > 0 AND c.consorcio <> \'STPC\'';
+ if (args.consorcioName?.length) {
+ qWhere += ` AND c.consorcio IN ('${args.consorcioName.join("', '")}')`
+ }
return qWhere;
}
private getQueryArgsOperadora(args: IBigqueryFindOrdemPagamento) {
const startDate = args.startDate.toISOString().slice(0, 10);
const endDate = args.endDate.toISOString().slice(0, 10);
- const qWhere =
- `t.data_ordem BETWEEN '${startDate}' AND '${endDate}' AND o.tipo_documento = 'CPF' ` +
+ let qWhere =
+ `date(t.datetime_captura) BETWEEN '${startDate}' AND '${endDate}' AND o.tipo_documento = 'CPF' ` +
'AND t.valor_total_transacao_liquido > 0';
+ if (args.consorcioName?.length) {
+ qWhere += ` AND c.consorcio IN ('${args.consorcioName.join("', '")}')`;
+ }
return qWhere;
}
}
diff --git a/src/bigquery/repositories/bigquery-transacao.repository.ts b/src/bigquery/repositories/bigquery-transacao.repository.ts
index ae9c456d1..a389f2e63 100644
--- a/src/bigquery/repositories/bigquery-transacao.repository.ts
+++ b/src/bigquery/repositories/bigquery-transacao.repository.ts
@@ -24,7 +24,7 @@ export interface IBqFindTransacao {
valor_pagamento?: number[] | null | ['>=' | '<=' | '>' | '<', number] | 'NOT NULL';
id_transacao?: string[] | null;
id_operadora?: string[];
- nomeConsorcio?: { in?: string[], notIn?: string[]};
+ nomeConsorcio?: { in?: string[]; notIn?: string[] };
}
@Injectable()
@@ -46,6 +46,96 @@ export class BigqueryTransacaoRepository {
return transacoes;
}
+ public async findManyByOrdemPagamentoIdIn(ordemPagamentoIds: number[], cpfCnpj: string | undefined, isAdmin: boolean): Promise {
+ let query = `SELECT CAST(t.datetime_transacao AS STRING) datetime_transacao,
+ CAST(t.datetime_processamento AS STRING) datetime_processamento,
+ t.valor_pagamento valor_pagamento,
+ t.valor_transacao valor_transacao,
+ t.tipo_pagamento,
+ CASE t.tipo_transacao_smtr
+ when 'Débito EMV' then 'Integral'
+ else t.tipo_transacao_smtr
+ end tipo_transacao_smtr,
+ t.tipo_transacao
+ FROM \`rj-smtr.br_rj_riodejaneiro_bilhetagem.transacao\` t
+ LEFT JOIN \`rj-smtr.cadastro.operadoras\` o
+ ON o.id_operadora = t.id_operadora
+ LEFT JOIN \`rj-smtr.cadastro.consorcios\` c ON c.id_consorcio = t.id_consorcio
+ WHERE 1 = 1
+ AND t.valor_pagamento
+ > 0
+ AND t.id_ordem_pagamento_consorcio_operador_dia IN UNNEST(?)`;
+
+ function mapBigQueryTransacao(item: any) {
+ const bigqueryTransacao = new BigqueryTransacao();
+ bigqueryTransacao.datetime_transacao = item.datetime_transacao;
+ bigqueryTransacao.datetime_processamento = item.datetime_processamento;
+ bigqueryTransacao.valor_pagamento = item.valor_pagamento;
+ bigqueryTransacao.valor_transacao = item.valor_transacao;
+ bigqueryTransacao.tipo_pagamento = item.tipo_pagamento;
+ bigqueryTransacao.tipo_transacao = item.tipo_transacao;
+ bigqueryTransacao.tipo_transacao_smtr = item.tipo_transacao_smtr;
+ return bigqueryTransacao;
+ }
+
+ if (!isAdmin) {
+ query += ` AND CAST(o.documento AS STRING) = ?`;
+ const queryResult = await this.bigqueryService.query(BigquerySource.smtr, query, [ordemPagamentoIds]);
+ return queryResult.map((item: any) => {
+ return mapBigQueryTransacao(item);
+ });
+ } else {
+ const queryResult = await this.bigqueryService.query(BigquerySource.smtr, query, [ordemPagamentoIds, cpfCnpj]);
+ return queryResult.map((item: any) => {
+ return mapBigQueryTransacao(item);
+ });
+ }
+ }
+
+ public async findManyByOrdemPagamentoIdInGroupedByTipoTransacao(ordemPagamentoId: (number | undefined)[], cpfCnpj: string | undefined, isAdmin: boolean): Promise {
+ let query = `SELECT ROUND(t.valor_pagamento, 2) valor_pagamento,
+ ROUND(t.valor_transacao, 2) valor_transacao,
+ t.tipo_pagamento,
+ t.tipo_transacao
+ FROM \`rj-smtr.br_rj_riodejaneiro_bilhetagem.transacao\` t
+ LEFT JOIN \`rj-smtr.cadastro.operadoras\` o
+ ON o.id_operadora = t.id_operadora
+ LEFT JOIN \`rj-smtr.cadastro.consorcios\` c ON c.id_consorcio = t.id_consorcio
+ WHERE 1 = 1
+ AND t.valor_pagamento
+ > 0
+ AND t.id_ordem_pagamento_consorcio_operador_dia IN UNNEST(?)`;
+
+ const ordensPagamentoIdStr = ordemPagamentoId.map((id) => id?.toString());
+
+ function mapBigQueryTransacaoAgrupado(item: any) {
+ const bigqueryTransacao = new BigqueryTransacao();
+ bigqueryTransacao.valor_pagamento = item.valor_pagamento;
+ bigqueryTransacao.valor_transacao = item.valor_transacao;
+ bigqueryTransacao.tipo_pagamento = item.tipo_pagamento;
+ bigqueryTransacao.tipo_transacao = item.tipo_transacao;
+ Object.keys(bigqueryTransacao).map((key) => {
+ if (bigqueryTransacao[key] === null) {
+ bigqueryTransacao[key] = undefined;
+ }
+ });
+ return bigqueryTransacao;
+ }
+
+ if (!isAdmin) {
+ query += ` AND CAST(o.documento AS STRING) = ?`;
+ const queryResult = await this.bigqueryService.query(BigquerySource.smtr, query, [ordensPagamentoIdStr, cpfCnpj]);
+ return queryResult.map((item: any) => {
+ return mapBigQueryTransacaoAgrupado(item);
+ });
+ } else {
+ const queryResult = await this.bigqueryService.query(BigquerySource.smtr, query, [ordensPagamentoIdStr]);
+ return queryResult.map((item: any) => {
+ return mapBigQueryTransacaoAgrupado(item);
+ });
+ }
+ }
+
private async queryData(args?: IBqFindTransacao): Promise<{
data: BigqueryTransacao[]; //
countAll: number;
@@ -54,41 +144,40 @@ export class BigqueryTransacaoRepository {
const qArgs = this.getQueryArgs(isBqProd, args);
const query =
`
- SELECT
- CAST(t.data AS STRING) AS \`data\`,
- t.hora,
- CAST(t.datetime_captura AS STRING) AS datetime_captura,
- CAST(t.datetime_transacao AS STRING) AS datetime_transacao,
- CAST(t.datetime_processamento AS STRING) AS datetime_processamento,
- t.datetime_captura AS captureDateTime,
- t.modo,
- t.sentido,
- t.id_veiculo,
- t.id_cliente,
- t.id_transacao,
- t.${qArgs.tTipoPgto} AS tipo_pagamento,
- t.tipo_transacao_smtr AS tipo_transacao,
- t.id_tipo_integracao,
- t.id_integracao,
- t.latitude,
- t.longitude,
- t.stop_id,
- t.stop_lat,
- t.stop_lon,
- t.valor_transacao,
- t.valor_pagamento,
- t.versao AS bqDataVersion,
- t.consorcio,
- o.operadora_completo AS operadora,
- t.id_consorcio,
- t.id_operadora,
- o.documento AS operadoraCpfCnpj,
- c.cnpj AS consorcioCnpj,
- 'ok' AS status,
- t.id_ordem_pagamento
- FROM \`${qArgs.transacao}\` t\n
- LEFT JOIN \`rj-smtr.cadastro.operadoras\` o ON o.id_operadora = t.id_operadora
- LEFT JOIN \`rj-smtr.cadastro.consorcios\` c ON c.id_consorcio = t.id_consorcio
+ SELECT CAST(t.data AS STRING) AS \`data\`, t.hora,
+ CAST(t.datetime_captura AS STRING) AS datetime_captura,
+ CAST(t.datetime_transacao AS STRING) AS datetime_transacao,
+ CAST(t.datetime_processamento AS STRING) AS datetime_processamento,
+ t.datetime_captura AS captureDateTime,
+ t.modo,
+ t.sentido,
+ t.id_veiculo,
+ t.id_cliente,
+ t.id_transacao,
+ t.${qArgs.tTipoPgto} AS tipo_pagamento,
+ t.tipo_transacao_smtr AS tipo_transacao,
+ t.id_tipo_integracao,
+ t.id_integracao,
+ t.latitude,
+ t.longitude,
+ t.stop_id,
+ t.stop_lat,
+ t.stop_lon,
+ t.valor_transacao,
+ t.valor_pagamento,
+ t.versao AS bqDataVersion,
+ t.consorcio,
+ o.operadora_completo AS operadora,
+ t.id_consorcio,
+ t.id_operadora,
+ o.documento AS operadoraCpfCnpj,
+ c.cnpj AS consorcioCnpj,
+ 'ok' AS status,
+ t.id_ordem_pagamento
+ FROM \` ${qArgs.transacao}\` t
+ LEFT JOIN \`rj-smtr.cadastro.operadoras\` o
+ ON o.id_operadora = t.id_operadora
+ LEFT JOIN \`rj-smtr.cadastro.consorcios\` c ON c.id_consorcio = t.id_consorcio
` +
'\n' +
qArgs.joinIntegracao +
diff --git a/src/bigquery/services/bigquery-ordem-pagamento.service.spec.ts b/src/bigquery/services/bigquery-ordem-pagamento.service.spec.ts
new file mode 100644
index 000000000..7fe4de2cc
--- /dev/null
+++ b/src/bigquery/services/bigquery-ordem-pagamento.service.spec.ts
@@ -0,0 +1,106 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { BigqueryOrdemPagamentoService } from './bigquery-ordem-pagamento.service';
+import { BigqueryOrdemPagamentoRepository } from '../repositories/bigquery-ordem-pagamento.repository';
+import { BigqueryOrdemPagamentoDTO } from '../dtos/bigquery-ordem-pagamento.dto';
+
+function getMockData() {
+ const mockData: BigqueryOrdemPagamentoDTO[] = [
+ {
+ id: 1,
+ dataOrdem: '2024-11-15',
+ dataPagamento: '2024-11-20',
+ idConsorcio: '1',
+ consorcio: 'Consorcio1',
+ idOperadora: '1',
+ operadora: 'Operadora1',
+ servico: 'Servico1',
+ idOrdemPagamento: '1',
+ idOrdemRessarcimento: '2',
+ quantidadeTransacaoDebito: 10,
+ valorDebito: 500,
+ quantidadeTransacaoEspecie: 5,
+ valorEspecie: 200,
+ quantidadeTransacaoGratuidade: 2,
+ valorGratuidade: 0,
+ quantidadeTransacaoIntegracao: 3,
+ valorIntegracao: 150,
+ quantidadeTransacaoRateioCredito: 1,
+ valorRateioCredito: 50,
+ quantidadeTransacaoRateioDebito: 1,
+ valorRateioDebito: 30,
+ quantidadeTotalTransacao: 22,
+ valorTotalTransacaoBruto: 930,
+ valorDescontoTaxa: 30,
+ valorTotalTransacaoLiquido: 900,
+ quantidadeTotalTransacaoCaptura: 22,
+ valorTotalTransacaoCaptura: 930,
+ indicadorOrdemValida: true,
+ versao: '1.0',
+ operadoraTipoDocumento: 'CPF',
+ operadoraCpfCnpj: '12345678901',
+ consorcioCnpj: '12345678000199',
+ tipoFavorecido: null,
+ datetimeUltimaAtualizacao: new Date('2024-11-15T00:00:00Z'),
+ dataCaptura: new Date('2024-11-15T00:00:00Z')
+ },
+ ];
+ return mockData;
+}
+
+describe('BigqueryOrdemPagamentoService', () => {
+ let service: BigqueryOrdemPagamentoService;
+ let repository: BigqueryOrdemPagamentoRepository;
+
+ beforeEach(async () => {
+ const module: TestingModule = await Test.createTestingModule({
+ providers: [
+ BigqueryOrdemPagamentoService,
+ {
+ provide: BigqueryOrdemPagamentoRepository,
+ useValue: {
+ findMany: jest.fn(),
+ },
+ },
+ ],
+ }).compile();
+
+ service = module.get(BigqueryOrdemPagamentoService);
+ repository = module.get(BigqueryOrdemPagamentoRepository);
+ });
+
+ it('should be defined', () => {
+ expect(service).toBeDefined();
+ });
+
+ it('should get data from the specified date range', async () => {
+ const dataCapturaInicial = new Date('2024-11-15');
+ const dataCapturaFinal = new Date('2024-11-21');
+ const mockData = getMockData();
+ jest.spyOn(repository, 'findMany').mockResolvedValue(mockData);
+
+ const result = await service.getFromWeek(dataCapturaInicial, dataCapturaFinal);
+
+ expect(result).toEqual(mockData);
+ expect(repository.findMany).toHaveBeenCalledWith({
+ startDate: dataCapturaInicial,
+ endDate: dataCapturaFinal,
+ });
+ });
+
+ it('should apply the filter when provided', async () => {
+ const dataCapturaInicial = new Date('2024-11-15');
+ const dataCapturaFinal = new Date('2024-11-21');
+ const filter = { consorcioName: ['Consorcio1'] };
+ const mockData: BigqueryOrdemPagamentoDTO[] = getMockData();
+ jest.spyOn(repository, 'findMany').mockResolvedValue(mockData);
+
+ const result = await service.getFromWeek(dataCapturaInicial, dataCapturaFinal, 0, filter);
+
+ expect(result).toEqual(mockData);
+ expect(repository.findMany).toHaveBeenCalledWith({
+ startDate: dataCapturaInicial,
+ endDate: dataCapturaFinal,
+ ...filter,
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/bigquery/services/bigquery-ordem-pagamento.service.ts b/src/bigquery/services/bigquery-ordem-pagamento.service.ts
index 95a06d69a..9d2247b18 100644
--- a/src/bigquery/services/bigquery-ordem-pagamento.service.ts
+++ b/src/bigquery/services/bigquery-ordem-pagamento.service.ts
@@ -3,6 +3,7 @@ import { isFriday, nextFriday, subDays } from 'date-fns';
import { BigqueryOrdemPagamentoDTO } from '../dtos/bigquery-ordem-pagamento.dto';
import { BigqueryOrdemPagamentoRepository } from '../repositories/bigquery-ordem-pagamento.repository';
import { CustomLogger } from 'src/utils/custom-logger';
+import { IBigqueryFindOrdemPagamento } from '../interfaces/bigquery-find-ordem-pagamento.interface';
@Injectable()
export class BigqueryOrdemPagamentoService {
@@ -13,17 +14,17 @@ export class BigqueryOrdemPagamentoService {
/**
* Get data from current payment week (qui-qua). Also with older days.
*/
- public async getFromWeek(dataOrdemInicial: Date, dataOrdemFinal: Date, daysBefore = 0): Promise {
+ public async getFromWeek(dataCapturaInicial: Date, dataCapturaFinal: Date, daysBefore = 0, filter?: { consorcioName?: string[] }): Promise {
const today = new Date();
let startDate: Date;
let endDate: Date;
- if (dataOrdemInicial != undefined && dataOrdemFinal != undefined) {
- startDate = new Date(dataOrdemInicial);
- endDate = new Date(dataOrdemFinal);
- } else if (dataOrdemInicial != undefined && dataOrdemFinal == undefined) {
- startDate = new Date(dataOrdemInicial);
- endDate = new Date(dataOrdemInicial);
+ if (dataCapturaInicial != undefined && dataCapturaFinal != undefined) {
+ startDate = new Date(dataCapturaInicial);
+ endDate = new Date(dataCapturaFinal);
+ } else if (dataCapturaInicial != undefined && dataCapturaFinal == undefined) {
+ startDate = new Date(dataCapturaInicial);
+ endDate = new Date(dataCapturaInicial);
} else {
//Sexta a Quinta
const friday = isFriday(today) ? today : nextFriday(today);
@@ -34,8 +35,15 @@ export class BigqueryOrdemPagamentoService {
await this.bigqueryOrdemPagamentoRepository.findMany({
startDate: startDate,
endDate: endDate,
+ ...(filter ? filter : {}),
})
- ).map((i) => ({ ...i } as BigqueryOrdemPagamentoDTO));
+ ).map((i) => ({ ...i } as BigqueryOrdemPagamentoDTO))
+ .map((ordem) => {
+ if (ordem.dataCaptura) {
+ ordem.dataCaptura = new Date(ordem.dataCaptura);
+ }
+ return ordem;
+ });
return ordemPgto;
}
}
diff --git a/src/bigquery/services/bigquery-transacao.service.spec.ts b/src/bigquery/services/bigquery-transacao.service.spec.ts
new file mode 100644
index 000000000..752915ada
--- /dev/null
+++ b/src/bigquery/services/bigquery-transacao.service.spec.ts
@@ -0,0 +1,82 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { BigqueryTransacaoService } from './bigquery-transacao.service';
+import { BigqueryTransacaoRepository } from '../repositories/bigquery-transacao.repository';
+import { BigqueryTransacao } from '../entities/transacao.bigquery-entity';
+import { Role } from '../../roles/entities/role.entity';
+
+describe('BigqueryTransacaoService', () => {
+ let service: BigqueryTransacaoService;
+ let repository: BigqueryTransacaoRepository;
+
+ beforeEach(async () => {
+ const module: TestingModule = await Test.createTestingModule({
+ providers: [
+ BigqueryTransacaoService,
+ {
+ provide: BigqueryTransacaoRepository,
+ useValue: {
+ findManyByOrdemPagamentoId: jest.fn(),
+ },
+ },
+ ],
+ }).compile();
+
+ service = module.get(BigqueryTransacaoService);
+ repository = module.get(BigqueryTransacaoRepository);
+ });
+
+ it('should be defined', () => {
+ expect(service).toBeDefined();
+ });
+
+ it('should return transactions by ordemPagamentoId', async () => {
+ const ordemPagamentoId = 1;
+ const mockData: BigqueryTransacao[] = [
+ {
+ id: 1,
+ data: '2024-11-15',
+ hora: 10,
+ datetime_transacao: '2024-11-15T10:00:00Z',
+ datetime_processamento: '2024-11-15T10:05:00Z',
+ datetime_captura: '2024-11-15T10:10:00Z',
+ modo: 'online',
+ id_consorcio: '1',
+ consorcio: 'Consorcio1',
+ id_operadora: '1',
+ operadoraCpfCnpj: '12345678901',
+ consorcioCnpj: '12345678000199',
+ operadora: 'Operadora1',
+ servico: 'Servico1',
+ sentido: 'Norte',
+ id_veiculo: 1,
+ id_cliente: '1',
+ id_transacao: '1',
+ tipo_pagamento: 'debito',
+ tipo_transacao: 'compra',
+ tipo_gratuidade: null,
+ tipo_integracao: null,
+ id_integracao: null,
+ latitude: null,
+ longitude: null,
+ stop_id: null,
+ stop_lat: null,
+ stop_lon: null,
+ valor_transacao: 100,
+ valor_pagamento: 100,
+ versao: '1.0',
+ id_ordem_pagamento: 1,
+ },
+ ];
+
+ jest.spyOn(repository, 'findManyByOrdemPagamentoIdIn').mockResolvedValue(mockData);
+
+ const role = new Role();
+ role.name = 'dummy';
+ role.id = 1;
+ const user = { user: { id: 1, role: role } };
+ const result = await service.findByOrdemPagamentoIdIn([ordemPagamentoId], '99999999999', { ...user } as any);
+
+ expect(result).toEqual(mockData);
+ expect(repository.findManyByOrdemPagamentoIdIn).toHaveBeenCalledWith([ordemPagamentoId], '99999999999', false);
+ });
+});
\ No newline at end of file
diff --git a/src/bigquery/services/bigquery-transacao.service.ts b/src/bigquery/services/bigquery-transacao.service.ts
index f519bed3b..901ae173a 100644
--- a/src/bigquery/services/bigquery-transacao.service.ts
+++ b/src/bigquery/services/bigquery-transacao.service.ts
@@ -2,6 +2,8 @@ import { Injectable } from '@nestjs/common';
import { CustomLogger } from 'src/utils/custom-logger';
import { BigqueryTransacao } from '../entities/transacao.bigquery-entity';
import { BigqueryTransacaoRepository, IBqFindTransacao } from '../repositories/bigquery-transacao.repository';
+import { IRequest } from '../../utils/interfaces/request.interface';
+import { isUser } from '../../utils/request-utils';
@Injectable()
export class BigqueryTransacaoService {
@@ -61,4 +63,12 @@ export class BigqueryTransacaoService {
});
callback(transacoes);
}
+
+ public async findByOrdemPagamentoIdIn(ordemPagamentoIds: number[], cpfCnpj: string | undefined, request: IRequest): Promise {
+ return await this.bigqueryTransacaoRepository.findManyByOrdemPagamentoIdIn(ordemPagamentoIds, cpfCnpj, !isUser(request));
+ }
+
+ public async findManyByOrdemPagamentoIdInGroupedByTipoTransacao(ordensPagamentoIds: (number | undefined)[], cpfCnpj: string | undefined, request: IRequest): Promise {
+ return await this.bigqueryTransacaoRepository.findManyByOrdemPagamentoIdInGroupedByTipoTransacao(ordensPagamentoIds, cpfCnpj, !isUser(request));
+ }
}
diff --git a/src/cnab/cnab-manutencao.controller.ts b/src/cnab/cnab-manutencao.controller.ts
deleted file mode 100644
index 32c6c94c2..000000000
--- a/src/cnab/cnab-manutencao.controller.ts
+++ /dev/null
@@ -1,282 +0,0 @@
-import { BadRequestException, Controller, Get, HttpCode, HttpStatus, NotImplementedException, ParseArrayPipe, Query, UseGuards } from '@nestjs/common';
-import { AuthGuard } from '@nestjs/passport';
-import { ApiBearerAuth, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
-import { Roles } from 'src/roles/roles.decorator';
-import { RoleEnum } from 'src/roles/roles.enum';
-import { RolesGuard } from 'src/roles/roles.guard';
-import { ApiDescription } from 'src/utils/api-param/description-api-param';
-import { CustomLogger } from 'src/utils/custom-logger';
-import { ParseArrayPipe as ParseArrayPipe1 } from 'src/utils/pipes/parse-array.pipe';
-import { ParseDatePipe } from 'src/utils/pipes/parse-date.pipe';
-import { ParseEnumPipe } from 'src/utils/pipes/parse-enum.pipe';
-import { ParseNumberPipe } from 'src/utils/pipes/parse-number.pipe';
-import { CnabService } from './cnab.service';
-import { HeaderArquivoStatus } from './enums/pagamento/header-arquivo-status.enum';
-
-@ApiTags('Manutenção')
-@Controller({
- path: 'manutencao/cnab',
- version: '1',
-})
-export class CnabManutencaoController {
- private logger = new CustomLogger(CnabManutencaoController.name, { timestamp: true });
-
- constructor(
- private readonly cnabService: CnabService, //
- ) {}
-
- @Get('generateRemessaLancamento')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta a geração e envio de remessa - que normalmente é feita via cronjob.' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'dataOrdemInicial', type: String, required: false, description: ApiDescription({ _: 'Data da Ordem de Pagamento Inicial - salvar transações', example: '2024-07-15' }) })
- @ApiQuery({ name: 'dataOrdemFinal', type: String, required: false, description: ApiDescription({ _: 'Data da Ordem de Pagamento Final - salvar transações', example: '2024-07-16' }) })
- @ApiQuery({ name: 'dataPagamento', type: String, required: false, description: ApiDescription({ _: 'Data de pagamento', default: 'O dia de hoje' }) })
- @ApiQuery({ name: 'isConference', type: Boolean, required: true, description: 'Conferencia - Se o remessa será gerado numa tabela de teste.', example: true })
- @ApiQuery({ name: 'isCancelamento', type: Boolean, required: true, description: 'Cancelamento', example: false })
- @ApiQuery({ name: 'isTeste', type: Boolean, required: true, description: 'Define se o CNAB Remessa usará o parâmetro de Teste', example: false })
- @ApiQuery({ name: 'nsaInicial', type: Number, required: false, description: ApiDescription({ default: 'O NSA atual' }) })
- @ApiQuery({ name: 'nsaFinal', type: Number, required: false, description: ApiDescription({ default: 'nsaInicial' }) })
- @ApiQuery({ name: 'dataCancelamento', type: String, required: false, description: ApiDescription({ _: 'Data de vencimento da transação a ser cancelada (DetalheA).', 'Required if': 'isCancelamento = true' }), example: '2024-07-16' })
- async getGenerateRemessaLancamento(
- @Query('dataOrdemInicial', new ParseDatePipe({ transform: true, optional: true })) _dataOrdemInicial: any, // Date
- @Query('dataOrdemFinal', new ParseDatePipe({ transform: true, optional: true })) _dataOrdemFinal: any, // Date
- @Query('dataPagamento', new ParseDatePipe({ transform: true, optional: true })) dataPagamento: Date | undefined, // Date | undefined
- @Query('isConference') isConference: boolean,
- @Query('isCancelamento') isCancelamento: boolean,
- @Query('isTeste') isTeste: boolean,
- @Query('nsaInicial', new ParseNumberPipe({ min: 1, optional: true })) nsaInicial: number | undefined,
- @Query('nsaFinal', new ParseNumberPipe({ min: 1, optional: true })) nsaFinal: number | undefined,
- @Query('dataCancelamento', new ParseDatePipe({ transform: true, optional: true })) _dataCancelamento: any, // Date | undefined
- ) {
- const dataOrdemInicial = _dataOrdemInicial as Date | undefined;
- const dataOrdemFinal = _dataOrdemFinal as Date | undefined;
- const dataCancelamento = _dataCancelamento as Date | undefined;
-
- if (isCancelamento && !dataCancelamento) {
- throw new BadRequestException('dataCancelamento é obrigatório se isCancelamento = true');
- }
-
- return new NotImplementedException();
- // return await this.cnabService.generateRemessaLancamento({
- // dataOrdemInicial,
- // dataOrdemFinal,
- // dataPgto: dataPagamento,
- // isConference,
- // isCancelamento,
- // isTeste,
- // nsaInicial,
- // nsaFinal,
- // dataCancelamento,
- // });
- }
-
- @Get('generateRemessaJae')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta a geração e envio de remessa - que normalmente é feita via cronjob' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'dataOrdemInicial', description: ApiDescription({ _: 'Data da Ordem de Pagamento Inicial - salvar transações', example: '2024-07-15' }), required: true, type: String })
- @ApiQuery({ name: 'dataOrdemFinal', description: ApiDescription({ _: 'Data da Ordem de Pagamento Final - salvar transações', example: '2024-07-16' }), required: true, type: String })
- @ApiQuery({ name: 'diasAnterioresOrdem', description: ApiDescription({ _: 'Procurar também por dias Anteriores a dataOrdemInicial - salvar transações', default: 0 }), required: false, type: Number, example: 7 })
- @ApiQuery({ name: 'consorcio', description: 'Nome do consorcio - salvar transações', required: true, type: String, example: 'Todos / Van / Empresa /Nome Consorcio' })
- @ApiQuery({ name: 'dt_pagamento', description: ApiDescription({ _: 'Data Pagamento', default: 'O dia de hoje' }), required: false, type: String })
- @ApiQuery({ name: 'isConference', description: 'Conferencia - Se o remessa será gerado numa tabela de teste.', required: true, type: Boolean, example: true })
- @ApiQuery({ name: 'isCancelamento', description: 'Cancelamento', required: true, type: Boolean, example: false })
- @ApiQuery({ name: 'isTeste', type: Boolean, required: true, description: 'Define se o CNAB Remessa usará o parâmetro de Teste', example: false })
- @ApiQuery({ name: 'nsaInicial', description: ApiDescription({ default: 'O NSA atual' }), required: false, type: Number })
- @ApiQuery({ name: 'nsaFinal', description: ApiDescription({ default: 'nsaInicial' }), required: false, type: Number })
- @ApiQuery({ name: 'dataCancelamento', description: ApiDescription({ _: 'Data de vencimento da transação a ser cancelada (DetalheA).', 'Required if': 'isCancelamento = true' }), required: false, type: String, example: '2024-07-16' })
- async getGenerateRemessaJae(
- @Query('dataOrdemInicial', new ParseDatePipe({ transform: true })) _dataOrdemInicial: any, // Date
- @Query('dataOrdemFinal', new ParseDatePipe({ transform: true })) _dataOrdemFinal: any, // Date
- @Query('diasAnterioresOrdem', new ParseNumberPipe({ min: 0, defaultValue: 0 })) diasAnteriores: number,
- @Query('consorcio') consorcio: string,
- @Query('dt_pagamento', new ParseDatePipe({ transform: true, optional: true })) _dataPgto: any, // Date | undefined
- @Query('isConference') isConference: boolean,
- @Query('isCancelamento') isCancelamento: boolean,
- @Query('isTeste') isTeste: boolean,
- @Query('nsaInicial', new ParseNumberPipe({ min: 1, optional: true })) nsaInicial: number | undefined,
- @Query('nsaFinal', new ParseNumberPipe({ min: 1, optional: true })) nsaFinal: number | undefined,
- @Query('dataCancelamento', new ParseDatePipe({ transform: true, optional: true })) _dataCancelamento: any, // Date | undefined
- ) {
- const dataOrdemInicial = _dataOrdemInicial as Date;
- const dataOrdemFinal = _dataOrdemFinal as Date;
- const dataPgto = _dataOrdemFinal as Date | undefined;
- const dataCancelamento = _dataCancelamento as Date | undefined;
-
- if (isCancelamento && !dataCancelamento) {
- throw new BadRequestException('dataCancelamento é obrigatório se isCancelamento = true');
- }
-
- return await this.cnabService.generateRemessaJae({
- dataOrdemInicial,
- dataOrdemFinal,
- diasAnteriores,
- consorcio,
- dataPgto,
- isConference,
- isCancelamento,
- isTeste,
- nsaInicial,
- nsaFinal,
- dataCancelamento,
- });
- }
-
- @Get('sendRemessa')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta o envio de remessa - que normalmente é feita via cronjob' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'headerArquivoIds', type: String, required: false, description: ApiDescription({ _: 'Ids do HeaderArquivo para gerar remessa', example: '1,2,3' }) })
- @ApiQuery({ name: 'headerArquivoStatus', enum: HeaderArquivoStatus, required: false, description: ApiDescription({ _: 'Buscar pos status do HeaderArquivo para gerar remessa' }) })
- async getSendRemessa(
- @Query('headerArquivoIds', new ParseArrayPipe({ items: Number, separator: ',', optional: true })) headerArquivoIds: number[] | undefined, //
- @Query('headerArquivoStatus', new ParseEnumPipe(HeaderArquivoStatus, { optional: true })) headerArquivoStatus: HeaderArquivoStatus | undefined, //
- ) {
- return await this.cnabService.findSendRemessas({ headerArquivoIds, status: headerArquivoStatus });
- }
-
- @Get('readRetornoPagamento')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta a leitura do retorno de pagamentos (Lançamento e Jaé) - que normalmente é feita via cronjob' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'folder', description: ApiDescription({ _: 'Pasta para ler os retornos', default: '`/retorno`' }), required: false, type: String })
- @ApiQuery({ name: 'maxItems', description: ApiDescription({ _: 'Número máximo de itens para ler', min: 1 }), required: false, type: Number })
- async getReadRetornoPagamento(
- @Query('folder') folder: string | undefined, //
- @Query('maxItems', new ParseNumberPipe({ min: 1, optional: true })) maxItems: number | undefined,
- ) {
- return await this.cnabService.readRetornoPagamento(folder, maxItems);
- }
-
- @Get('readRetornoExtrato')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta a leitura do retorno de extrato - que normalmente é feita via cronjob' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'folder', description: ApiDescription({ _: 'Pasta para ler os retornos', default: '`/retorno`' }), required: false, type: String })
- @ApiQuery({ name: 'maxItems', description: ApiDescription({ _: 'Número máximo de itens para ler', min: 1 }), required: false, type: Number })
- async getReadRetornoExtrato(
- @Query('folder') folder: string | undefined, //
- @Query('maxItems', new ParseNumberPipe({ min: 1, optional: true })) maxItems: number | undefined,
- ) {
- return await this.cnabService.readRetornoExtrato(folder, maxItems);
- }
-
- @Get('syncTransacaoViewOrdemPgto')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta o sincronismo de TransacaoView com as OrdensPagamento (ItemTransacaoAgrupado) - que normalmente é feia via cronjob' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'dataOrdemInicial', type: Date, required: false, description: 'Data da Ordem de Pagamento Inicial' })
- @ApiQuery({ name: 'dataOrdemFinal', type: Date, required: false, description: 'Data da Ordem de Pagamento Final' })
- @ApiQuery({ name: 'nomeFavorecido', type: String, required: false, description: 'Lista de nomes dos favorecidos' })
- @ApiQuery({ name: 'consorcio', type: String, required: false, description: ApiDescription({ _: 'Nome do consorcio - salvar transações', default: 'Todos' }), example: 'Nome Consorcio' })
- async getSyncTransacaoViewOrdemPgto(
- @Query('dataOrdemInicial', new ParseDatePipe({ transform: true, optional: true })) dataOrdemInicial: Date | undefined, //
- @Query('dataOrdemFinal', new ParseDatePipe({ transform: true, optional: true })) dataOrdemFinal: Date | undefined,
- @Query('nomeFavorecido', new ParseArrayPipe1({ transform: true, optional: true })) nomeFavorecido: string[] | undefined,
- @Query('consorcio', new ParseArrayPipe1({ transform: true, optional: true })) consorcio: string[] | undefined,
- ) {
- const dataOrdem_between = dataOrdemInicial && dataOrdemFinal && ([dataOrdemInicial, dataOrdemFinal] as [Date, Date]);
- return await this.cnabService.syncTransacaoViewOrdemPgto({ dataOrdem_between, nomeFavorecido, consorcio: { in: consorcio } });
- }
-
- @Get('clearSyncTransacaoView')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nLimpa o sincronismo de TransacaoView com as OrdensPagamento (ItemTransacaoAgrupado) - NÃO é usado no cronjob.' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'dataOrdemInicial', type: Date, required: false, description: 'Data da Ordem de Pagamento Inicial' })
- @ApiQuery({ name: 'dataOrdemFinal', type: Date, required: false, description: 'Data da Ordem de Pagamento Final' })
- @ApiQuery({ name: 'nomeFavorecido', type: String, required: false, description: 'Lista de nomes dos favorecidos' })
- @ApiQuery({ name: 'consorcio', type: String, required: false, description: ApiDescription({ _: 'Nome do consorcio - salvar transações', default: 'Todos' }), example: 'Nome Consorcio' })
- async getClearSyncTransacaoView(
- @Query('dataOrdemInicial', new ParseDatePipe({ transform: true, optional: true })) dataOrdemInicial: Date | undefined, //
- @Query('dataOrdemFinal', new ParseDatePipe({ transform: true, optional: true })) dataOrdemFinal: Date | undefined,
- @Query('nomeFavorecido', new ParseArrayPipe1({ transform: true, optional: true })) nomeFavorecido: string[] | undefined,
- @Query('consorcio', new ParseArrayPipe1({ transform: true, optional: true })) consorcio: string[] | undefined,
- ) {
- const dataOrdem_between = dataOrdemInicial && dataOrdemFinal && ([dataOrdemInicial, dataOrdemFinal] as [Date, Date]);
- return await this.cnabService.clearSyncTransacaoView({ dataOrdem_between, nomeFavorecido, consorcio });
- }
-
- @Get('updateTransacaoViewBigquery')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Atualiza TransacaoView do Bigquery' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'dataOrdemInicial', type: Date, required: true, description: 'Data da Ordem de Pagamento Inicial' })
- @ApiQuery({ name: 'dataOrdemFinal', type: Date, required: true, description: 'Data da Ordem de Pagamento Final' })
- @ApiQuery({ name: 'consorcio', type: String, required: false, description: ApiDescription({ _: 'Nome do consorcio - salvar transações', default: 'Todos' }), example: 'Todos / Van / Empresa / Nome Consorcio' })
- @ApiQuery({ name: 'idTransacao', type: String, required: false, description: 'Lista de idTransacao para atualizar' })
- async getUpdateTransacaoViewBigquery(
- @Query('dataOrdemInicial', new ParseDatePipe({ transform: true })) dataOrdemInicial: any, //
- @Query('dataOrdemFinal', new ParseDatePipe({ transform: true })) dataOrdemFinal: any,
- @Query('consorcio') consorcio: string | undefined,
- @Query('idTransacao', new ParseArrayPipe1({ optional: true })) idTransacao: string[], //
- ) {
- const _dataOrdemInicial: Date = dataOrdemInicial;
- const _dataOrdemFinal: Date = dataOrdemFinal;
- const _consorcio = consorcio || 'Todos';
- return await this.cnabService.updateTransacaoViewBigquery(_dataOrdemInicial, _dataOrdemFinal, 0, _consorcio, idTransacao);
- }
-
- @Get('deduplicateTransacaoView')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nRemove duplicatas de TransacaoView' })
- @ApiBearerAuth()
- async getDeduplicateTransacaoView() {
- return await this.cnabService.deduplicateTransacaoView();
- }
-
- @Get('updateTransacaoViewBigqueryValues')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nAtualiza os valores de TransacaoView existentes a a partir do Bigquery.' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'diasAnteriores', type: Number, required: false, description: 'Atualizar apenas os itens até N dias atrás' })
- @ApiQuery({ name: 'idOperadora', type: String, required: false, description: ApiDescription({ _: 'Pesquisar pelo idConsorcio para atualizar', example: '8000123,8000456' }) })
- async getUpdateTransacaoViewBigqueryValues(
- @Query('diasAnteriores', new ParseNumberPipe({ optional: true })) diasAnteriores: number | undefined, //
- @Query('idOperadora', new ParseArrayPipe1({ optional: true })) idOperadora: string[] | undefined, //
- ) {
- return await this.cnabService.updateTransacaoViewBigqueryValues(diasAnteriores, idOperadora);
- }
-
- @Get('updateTransacaoViewBigqueryValues')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nAtualiza os valores de TransacaoView existentes a a partir do Bigquery.' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'dataOrdemInicial', type: Date, required: true, description: 'Data da Ordem de Pagamento Inicial' })
- @ApiQuery({ name: 'dataOrdemFinal', type: Date, required: true, description: 'Data da Ordem de Pagamento Final' })
- @ApiQuery({ name: 'consorcio', type: String, required: false, description: ApiDescription({ _: 'Nome do consorcio - salvar transações', default: 'Todos' }), example: 'Todos / Van / Empresa /Nome Consorcio' })
- async getSaveTransacoesJae(
- @Query('dataOrdemInicial', new ParseDatePipe({ transform: true })) dataOrdemInicial: any, //
- @Query('dataOrdemFinal', new ParseDatePipe({ transform: true })) dataOrdemFinal: any,
- @Query('consorcio') consorcio: string | undefined,
- @Query('idTransacao', new ParseArrayPipe1({ optional: true })) idTransacao: string[], //
- ) {
- const _dataOrdemInicial: Date = dataOrdemInicial;
- const _dataOrdemFinal: Date = dataOrdemFinal;
- const _consorcio = consorcio || 'Todos';
- return await this.cnabService.saveTransacoesJae(_dataOrdemInicial, _dataOrdemFinal, 0, _consorcio);
- }
-}
diff --git a/src/cnab/cnab.module.ts b/src/cnab/cnab.module.ts
index a27f899b8..645557dbf 100644
--- a/src/cnab/cnab.module.ts
+++ b/src/cnab/cnab.module.ts
@@ -70,10 +70,24 @@ import { PagamentosPendentesService } from './service/pagamento/pagamentos-pende
import { RemessaRetornoService } from './service/pagamento/remessa-retorno.service';
import { TransacaoAgrupadoService } from './service/pagamento/transacao-agrupado.service';
import { TransacaoService } from './service/pagamento/transacao.service';
-import { CnabManutencaoController } from './cnab-manutencao.controller';
import { PagamentoIndevidoService } from 'src/pagamento_indevido/service/pgamento-indevido-service';
import { PagamentoIndevidoRepository } from 'src/pagamento_indevido/repository/pagamento-indevido.repository';
import { PagamentoIndevido } from 'src/pagamento_indevido/entity/pagamento-indevido.entity';
+import { OrdemPagamento } from './novo-remessa/entity/ordem-pagamento.entity';
+import { OrdemPagamentoAgrupado } from './novo-remessa/entity/ordem-pagamento-agrupado.entity';
+import { OrdemPagamentoService } from './novo-remessa/service/ordem-pagamento.service';
+import { OrdemPagamentoRepository } from './novo-remessa/repository/ordem-pagamento.repository';
+import { OrdemPagamentoAgrupadoRepository } from './novo-remessa/repository/ordem-pagamento-agrupado.repository';
+import { OrdemPagamentoAgrupadoService } from './novo-remessa/service/ordem-pagamento-agrupado.service';
+import {
+ OrdemPagamentoAgrupadoHistoricoRepository
+} from './novo-remessa/repository/ordem-pagamento-agrupado-historico.repository';
+import { OrdemPagamentoAgrupadoHistorico } from './novo-remessa/entity/ordem-pagamento-agrupado-historico.entity';
+import { OrdemPagamentoController } from './novo-remessa/controller/ordem-pagamento.controller';
+import { RemessaService } from './novo-remessa/service/remessa.service';
+import { RetornoService } from './novo-remessa/service/retorno.service';
+import { DistributedLockService } from './novo-remessa/service/distributed-lock.service';
+import { DistributedLockRepository } from './novo-remessa/repository/distributed-lock.repository';
@Module({
imports: [
@@ -105,7 +119,10 @@ import { PagamentoIndevido } from 'src/pagamento_indevido/entity/pagamento-indev
ExtratoHeaderArquivo,
ExtratoHeaderLote,
ExtratoDetalheE,
- PagamentoIndevido
+ PagamentoIndevido,
+ OrdemPagamento,
+ OrdemPagamentoAgrupado,
+ OrdemPagamentoAgrupadoHistorico
]),
],
providers: [
@@ -152,7 +169,16 @@ import { PagamentoIndevido } from 'src/pagamento_indevido/entity/pagamento-indev
TransacaoRepository,
TransacaoService,
PagamentoIndevidoRepository,
- PagamentoIndevidoService
+ PagamentoIndevidoService,
+ OrdemPagamentoService,
+ OrdemPagamentoRepository,
+ OrdemPagamentoAgrupadoRepository,
+ OrdemPagamentoAgrupadoService,
+ OrdemPagamentoAgrupadoHistoricoRepository,
+ RemessaService,
+ RetornoService,
+ DistributedLockService,
+ DistributedLockRepository
],
exports: [
CnabService, //
@@ -193,8 +219,17 @@ import { PagamentoIndevido } from 'src/pagamento_indevido/entity/pagamento-indev
RemessaRetornoService,
OcorrenciaService,
PagamentoIndevidoRepository,
- PagamentoIndevidoService
+ PagamentoIndevidoService,
+ OrdemPagamentoService,
+ OrdemPagamentoRepository,
+ OrdemPagamentoAgrupadoRepository,
+ OrdemPagamentoAgrupadoService,
+ OrdemPagamentoAgrupadoHistoricoRepository,
+ RemessaService,
+ RetornoService,
+ DistributedLockService,
+ DistributedLockRepository
],
- controllers: [CnabController, CnabManutencaoController],
+ controllers: [CnabController, OrdemPagamentoController],
})
export class CnabModule {}
diff --git a/src/cnab/cnab.service.ts b/src/cnab/cnab.service.ts
index 677eed225..ecde37926 100644
--- a/src/cnab/cnab.service.ts
+++ b/src/cnab/cnab.service.ts
@@ -62,10 +62,11 @@ import { TransacaoAgrupadoService } from './service/pagamento/transacao-agrupado
import { TransacaoService } from './service/pagamento/transacao.service';
import { parseCnab240Extrato, parseCnab240Pagamento, stringifyCnab104File } from './utils/cnab/cnab-104-utils';
+
export interface ICnabInfo {
name: string;
content: string;
- headerArquivo: HeaderArquivoDTO;
+ headerArquivo: HeaderArquivoDTO | HeaderArquivo;
}
interface IFindRemessaArgs {
@@ -90,8 +91,7 @@ export class CnabService {
private extDetalheEService: ExtratoDetalheEService,
private extHeaderArquivoService: ExtratoHeaderArquivoService,
private extHeaderLoteService: ExtratoHeaderLoteService,
- private headerArquivoService: HeaderArquivoService,
- private headerArquivoConfService: HeaderArquivoService,
+ private headerArquivoService: HeaderArquivoService,
private headerLoteService: HeaderLoteService,
private itemTransacaoAgService: ItemTransacaoAgrupadoService,
private itemTransacaoService: ItemTransacaoService,
@@ -184,11 +184,16 @@ export class CnabService {
now = new Date();
const listCnab = await this.generateRemessa({
- tipo: PagadorContaEnum.ContaBilhetagem,
- dataPgto, isConference, isCancelamento, isTeste, nsaInicial, nsaFinal, dataCancelamento
+ tipo: PagadorContaEnum.ContaBilhetagem,
+ dataPgto,
+ isConference,
+ isCancelamento,
+ isTeste,
+ nsaInicial,
+ nsaFinal,
+ dataCancelamento,
});
-
duration.generateRemessa = formatDateInterval(new Date(), now);
now = new Date();
@@ -252,12 +257,9 @@ export class CnabService {
public async sendRemessa(listCnab: ICnabInfo[]) {
for (const cnab of listCnab) {
cnab.name = await this.sftpService.submitCnabRemessa(cnab.content);
- const remessaName = ((l = cnab.name.split('/')) => l.slice(l.length - 1)[0])();
- if (cnab.headerArquivo._isConf) {
- await this.headerArquivoConfService.save({ id: cnab.headerArquivo.id, remessaName, status: HeaderArquivoStatus._3_remessaEnviado });
- } else {
- await this.headerArquivoService.save({ id: cnab.headerArquivo.id, remessaName, status: HeaderArquivoStatus._3_remessaEnviado });
- }
+ const remessaName = ((l = cnab.name.split('/')) => l.slice(l.length - 1)[0])();
+ await this.headerArquivoService.save({ id: cnab.headerArquivo.id, remessaName,
+ status: HeaderArquivoStatus._3_remessaEnviado });
}
}
@@ -317,18 +319,16 @@ export class CnabService {
*
* Requirement: **Salvar novas transações Jaé** - {@link https://github.com/RJ-SMTR/api-cct/issues/207#issuecomment-1984421700 #207, items 3}
*/
- public async saveTransacoesJae(dataOrdemIncial: Date, dataOrdemFinal: Date, daysBefore = 0, consorcio: 'VLT' | 'Van' | 'Empresa' | 'Todos' | string) {
- const dataOrdemInicialDate = startOfDay(new Date(dataOrdemIncial));
- const dataOrdemFinalDate = endOfDay(new Date(dataOrdemFinal));
+ public async saveTransacoesJae(dataCapturaInicial: Date, dataCapturaFinal: Date, daysBefore = 0, consorcio: 'VLT' | 'Van' | 'Empresa' | 'Todos' | string) {
+ const dataCapturaInicialDate = startOfDay(new Date(dataCapturaInicial));
+ const dataCapturaFinalDate = endOfDay(new Date(dataCapturaFinal));
await this.updateAllFavorecidosFromUsers();
- const ordens = await this.bigqueryOrdemPagamentoService.getFromWeek(dataOrdemInicialDate, dataOrdemFinalDate, daysBefore);
+ const ordens = await this.bigqueryOrdemPagamentoService.getFromWeek(dataCapturaInicialDate, dataCapturaFinalDate, daysBefore);
let ordensFilter: BigqueryOrdemPagamentoDTO[];
if (consorcio.trim() === 'Empresa') {
- ordensFilter = ordens.filter((ordem) => ordem.consorcio.trim() !== 'VLT'
- && ordem.consorcio.trim() !== 'STPC' && ordem.consorcio.trim() !== 'STPL' && ordem.consorcio.trim() !=='TEC');
+ ordensFilter = ordens.filter((ordem) => ordem.consorcio.trim() !== 'VLT' && ordem.consorcio.trim() !== 'STPC' && ordem.consorcio.trim() !== 'STPL' && ordem.consorcio.trim() !== 'TEC');
} else if (consorcio.trim() === 'Van') {
- ordensFilter = ordens.filter((ordem) => ordem.consorcio.trim() === 'STPC'
- || ordem.consorcio.trim() === 'STPL' || ordem.consorcio.trim() === 'TEC');
+ ordensFilter = ordens.filter((ordem) => ordem.consorcio.trim() === 'STPC' || ordem.consorcio.trim() === 'STPL' || ordem.consorcio.trim() === 'TEC');
} else {
ordensFilter = ordens.filter((ordem) => ordem.consorcio === consorcio.trim());
}
@@ -344,6 +344,7 @@ export class CnabService {
/**
* Atualiza a tabela TransacaoView com dados novos ou atualizados do bigquery
*/
+
async updateTransacaoViewBigquery(dataOrdemIncial: Date, dataOrdemFinal: Date, daysBack = 0, consorcio: string = 'Todos', idTransacao: string[] = []) {
const METHOD = 'updateTransacaoViewBigquery';
const trs = await this.findBigqueryTransacao(dataOrdemIncial, dataOrdemFinal, daysBack, consorcio);
@@ -373,7 +374,31 @@ export class CnabService {
if (existing.length === 0) {
await queryRunner.manager.getRepository(TransacaoView).save(transacaoViewBq);
response.created += 1;
- } else {
+ } else {
+ if (existing.length > 1) {
+ await queryRunner.manager.getRepository(TransacaoView).remove(existing.slice(1));
+ response.deduplicated += 1;
+ }
+
+ if (
+ existing[0].idOrdemPagamento !== undefined &&
+ transacaoViewBq.idOrdemPagamento !== null
+ ) {
+ response.updated += 1;
+ await this.transacaoViewService.updateManyRaw(
+ [
+ {
+ id: existing[0].id,
+ idTransacao: existing[0].idTransacao,
+ idOrdemPagamento: Number(transacaoViewBq.idOrdemPagamento),
+ },
+ ],
+ ['idTransacao', 'idOrdemPagamento'],
+ 'idTransacao',
+ queryRunner.manager
+ );
+ }
+
if (!existing[0].valorPago && transacaoViewBq.valorPago != existing[0].valorPago) {
response.updated += 1;
await this.transacaoViewService.updateManyRaw(
@@ -432,11 +457,10 @@ export class CnabService {
const METHOD = 'saveAgrupamentos';
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
- let transacaoAg: Nullable=null;
+ let transacaoAg: Nullable = null;
try {
await queryRunner.startTransaction();
- this.logger.debug(`Salvando Agrupamento - ${JSON.stringify({ consorcio: ordem.consorcio,
- operadora: favorecido.nome, favorecidoCpfCnpj: ordem.favorecidoCpfCnpj })}`, METHOD);
+ this.logger.debug(`Salvando Agrupamento - ${JSON.stringify({ consorcio: ordem.consorcio, operadora: favorecido.nome, favorecidoCpfCnpj: ordem.favorecidoCpfCnpj })}`, METHOD);
transacaoAg = await this.transacaoAgService.findOne({
dataOrdem: ordem.getTransacaoAgrupadoDataOrdem(),
pagador: { id: pagador.id },
@@ -456,7 +480,7 @@ export class CnabService {
this.logger.debug('Fim Agrupamento Consorcio: ' + ordem.consorcio);
} catch (error) {
await queryRunner.rollbackTransaction();
- if(transacaoAg!==null){
+ if (transacaoAg !== null) {
transacaoAg.status.id = TransacaoStatusEnum.cancelado;
await this.transacaoAgService.save(transacaoAg);
}
@@ -483,7 +507,7 @@ export class CnabService {
},
...(ordem.consorcio.length || ordem.operadora.length
? // Se for Jaé, agrupa por vanzeiro ou empresa
- ordem.consorcio === 'STPC' || ordem.consorcio === 'STPL' || ordem.consorcio ==='TEC'
+ ordem.consorcio === 'STPC' || ordem.consorcio === 'STPL' || ordem.consorcio === 'TEC'
? { idOperadora: ordem.idOperadora }
: { idConsorcio: ordem.idConsorcio }
: // Se for Lançamento, agrupa por favorecido e dataOrdem
@@ -552,21 +576,17 @@ export class CnabService {
// await this.saveOrdens(ordensCb, 'contaBilhetagem');
// }
- public async generateRemessa(args: {
- tipo: PagadorContaEnum; dataPgto?: Date;
- isConference: boolean; isCancelamento: boolean;
- isTeste: boolean; nsaInicial?: number;
- nsaFinal?: number; dataCancelamento?: Date }): Promise {
- const currentNSA = await this.settingsService.getCurrentNSA(args.isTeste);
+ public async generateRemessa(args: { tipo: PagadorContaEnum; dataPgto?: Date; isConference: boolean; isCancelamento: boolean; isTeste: boolean; nsaInicial?: number; nsaFinal?: number; dataCancelamento?: Date }): Promise {
+ const currentNSA = await this.settingsService.getCurrentNSA(args.isTeste);
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
const listCnab: ICnabInfo[] = [];
try {
await queryRunner.startTransaction();
if (args.isCancelamento) {
- await this.geraRemssaCancelamento(args,listCnab,currentNSA);
+ await this.geraRemssaCancelamento(args, listCnab, currentNSA);
} else {
- await this.gerarRemessaPadrao(args,listCnab);
+ await this.gerarRemessaPadrao(args, listCnab);
}
await queryRunner.commitTransaction();
} catch (error) {
@@ -610,9 +630,8 @@ export class CnabService {
}
}
}
-
-
- private async geraRemssaCancelamento(args: any,listCnab: ICnabInfo[] = [],currentNSA){
+
+ private async geraRemssaCancelamento(args: any, listCnab: ICnabInfo[] = [], currentNSA) {
let nsaInicial = args.nsaInicial || currentNSA;
let nsaFinal = args.nsaFinal || nsaInicial;
if (this.validateCancel(args.nsaInicial, args.nsaFinal)) {
@@ -632,11 +651,10 @@ export class CnabService {
const headerLoteDTOs: HeaderLoteDTO[] = [];
let detalhes: CnabRegistros104Pgto[] = [];
for (const lote of lotes) {
- const headerLoteDTO = HeaderLoteDTO.fromHeaderArquivoDTO(headerArquivo,
- lote.pagador, lote.formaLancamento == '41' ? Cnab104FormaLancamento.TED : Cnab104FormaLancamento.CreditoContaCorrente, args.isTeste);
+ const headerLoteDTO = HeaderLoteDTO.fromHeaderArquivoDTO(headerArquivo, lote.pagador, lote.formaLancamento == '41' ? Cnab104FormaLancamento.TED : Cnab104FormaLancamento.CreditoContaCorrente, args.isTeste);
const detalhesA = (await this.detalheAService.findMany({ headerLote: { id: lote.id } })).sort((a, b) => a.nsr - b.nsr);
for (const detalheA of detalhesA) {
- const detalhe = await this.remessaRetornoService.saveDetalhes104(undefined,detalheA.numeroDocumentoEmpresa, headerLoteDTO, detalheA.itemTransacaoAgrupado, detalheA.nsr, false, detalheA.dataVencimento, true, detalheA);
+ const detalhe = await this.remessaRetornoService.saveDetalhes104(undefined, detalheA.numeroDocumentoEmpresa, headerLoteDTO, detalheA.itemTransacaoAgrupado, detalheA.nsr, false, detalheA.dataVencimento, true, detalheA);
if (detalhe) {
detalhes.push(detalhe);
}
@@ -645,7 +663,9 @@ export class CnabService {
headerLoteDTOs.push(headerLoteDTO);
detalhes = [];
}
- const cnab104 = CnabFile104PgtoDTO.fromDTO({ headerArquivoDTO: headerArquivo, headerLoteDTOs, isCancelamento: true,isTeste: args.isTeste });
+
+ const cnab104 = CnabFile104PgtoDTO.fromDTO({ headerArquivoDTO: headerArquivoDTO, headerLoteDTOs: headerLoteDTOs,
+ isCancelamento: true, isTeste: args.isTeste });
if (headerArquivo && cnab104) {
const [cnabStr] = stringifyCnab104File(cnab104, true, 'CnabPgtoRem');
if (!cnabStr) {
diff --git a/src/cnab/dto/cnab-240/104/cnab-header-arquivo-104.dto.ts b/src/cnab/dto/cnab-240/104/cnab-header-arquivo-104.dto.ts
index 78936abd8..664aa6239 100644
--- a/src/cnab/dto/cnab-240/104/cnab-header-arquivo-104.dto.ts
+++ b/src/cnab/dto/cnab-240/104/cnab-header-arquivo-104.dto.ts
@@ -52,7 +52,7 @@ export class CnabHeaderArquivo104DTO implements CnabHeaderArquivo104 {
Object.assign(this, dto);
}
- static fromDTO(headerArquivoDTO: HeaderArquivoDTO | HeaderArquivo, isTeste?: boolean): CnabHeaderArquivo104 {
+ static fromDTO(headerArquivoDTO: HeaderArquivoDTO, isTeste?: boolean): CnabHeaderArquivo104 {
const headerArquivo104: CnabHeaderArquivo104 = structuredClone(Cnab104PgtoTemplates.file104.registros.headerArquivo);
headerArquivo104.codigoBanco.value = headerArquivoDTO.codigoBanco;
headerArquivo104.numeroInscricao.value = headerArquivoDTO.numeroInscricao;
@@ -65,12 +65,13 @@ export class CnabHeaderArquivo104DTO implements CnabHeaderArquivo104 {
headerArquivo104.nomeEmpresa.value = headerArquivoDTO.nomeEmpresa;
headerArquivo104.tipoArquivo.value = headerArquivoDTO.tipoArquivo;
headerArquivo104.dataGeracaoArquivo.value = headerArquivoDTO.dataGeracao;
- headerArquivo104.horaGeracaoArquivo.value = headerArquivoDTO.horaGeracao;
+ headerArquivo104.horaGeracaoArquivo.value = headerArquivoDTO.dataGeracao;
headerArquivo104.nsa.value = headerArquivoDTO.nsa;
headerArquivo104.ambienteCliente.value = isTeste ? Cnab104AmbienteCliente.Teste : Cnab104AmbienteCliente.Producao;
return headerArquivo104;
}
+
codigoBanco: CnabField;
loteServico: CnabFieldAs;
codigoRegistro: CnabFieldAs;
diff --git a/src/cnab/dto/pagamento/detalhe-a.dto.ts b/src/cnab/dto/pagamento/detalhe-a.dto.ts
index 8d5e4f8c6..66dde6f14 100644
--- a/src/cnab/dto/pagamento/detalhe-a.dto.ts
+++ b/src/cnab/dto/pagamento/detalhe-a.dto.ts
@@ -12,6 +12,7 @@ import { getDateFromCnabName } from 'src/utils/date-utils';
import { DeepPartial } from 'typeorm';
import { HeaderLote } from '../../entity/pagamento/header-lote.entity';
import { CnabHeaderArquivo104 } from '../cnab-240/104/cnab-header-arquivo-104.dto';
+import { OrdemPagamentoAgrupadoHistorico } from 'src/cnab/novo-remessa/entity/ordem-pagamento-agrupado-historico.entity';
function isCreate(object: DetalheADTO): boolean {
return object.id === undefined;
@@ -24,7 +25,7 @@ export class DetalheADTO {
}
}
- static fromRemessa(detalheA: CnabDetalheA_104, existing: DetalheA | DetalheAConf | null, headerLoteId: number, itemTransacaoAg: ItemTransacaoAgrupado) {
+ static fromRemessa(detalheA: CnabDetalheA_104, existing: DetalheA | DetalheAConf | null, headerLoteId: number,itemTransacaoAg?: ItemTransacaoAgrupado, historico?: OrdemPagamentoAgrupadoHistorico) {
return new DetalheADTO({
...(existing ? { id: existing.id } : {}),
nsr: Number(detalheA.nsr.value),
@@ -45,7 +46,8 @@ export class DetalheADTO {
numeroParcela: getCnabFieldConverted(detalheA.numeroParcela),
dataEfetivacao: getCnabFieldConverted(detalheA.dataEfetivacao),
headerLote: { id: headerLoteId },
- itemTransacaoAgrupado: itemTransacaoAg,
+ ordemPagamentoAgrupadoHistorico: historico,
+ itemTransacaoAg: itemTransacaoAg
});
}
@@ -145,7 +147,10 @@ export class DetalheADTO {
@IsNotEmpty()
nsr?: number;
- itemTransacaoAgrupado?: ItemTransacaoAgrupado;
+ ordemPagamentoAgrupadoHistorico?: OrdemPagamentoAgrupadoHistorico;
+
+ itemTransacaoAg?: ItemTransacaoAgrupado;
+
retornoName?: string | null;
retornoDatetime?: Date | null;
}
diff --git a/src/cnab/dto/pagamento/header-arquivo.dto.ts b/src/cnab/dto/pagamento/header-arquivo.dto.ts
index 12eb7659e..9618c4ec0 100644
--- a/src/cnab/dto/pagamento/header-arquivo.dto.ts
+++ b/src/cnab/dto/pagamento/header-arquivo.dto.ts
@@ -4,7 +4,6 @@ import { HeaderArquivo } from 'src/cnab/entity/pagamento/header-arquivo.entity';
import { TransacaoAgrupado } from 'src/cnab/entity/pagamento/transacao-agrupado.entity';
import { Cnab104AmbienteCliente } from 'src/cnab/enums/104/cnab-104-ambiente-cliente.enum';
import { DeepPartial } from 'typeorm';
-import { Transacao } from '../../entity/pagamento/transacao.entity';
import { HeaderArquivoTipoArquivo } from 'src/cnab/enums/pagamento/header-arquivo-tipo-arquivo.enum';
import { Pagador } from 'src/cnab/entity/pagamento/pagador.entity';
import { Cnab104PgtoTemplates } from 'src/cnab/templates/cnab-240/104/pagamento/cnab-104-pgto-templates.const';
diff --git a/src/cnab/entity/pagamento/detalhe-a.entity.ts b/src/cnab/entity/pagamento/detalhe-a.entity.ts
index db439afc2..30dc4b5e2 100644
--- a/src/cnab/entity/pagamento/detalhe-a.entity.ts
+++ b/src/cnab/entity/pagamento/detalhe-a.entity.ts
@@ -7,6 +7,8 @@ import { ItemTransacaoAgrupado } from './item-transacao-agrupado.entity';
import { Ocorrencia } from './ocorrencia.entity';
import { isAfter } from 'date-fns';
import { getDateFromCnabName } from 'src/utils/date-utils';
+import { OrdemPagamentoAgrupadoHistorico } from 'src/cnab/novo-remessa/entity/ordem-pagamento-agrupado-historico.entity';
+import { Nullable } from 'src/utils/types/nullable.type';
/**
* Pagamento.DetalheA
@@ -16,12 +18,9 @@ export class DetalheA extends EntityHelper {
constructor(detalheA?: DeepPartial) {
super();
if (detalheA) {
- Object.assign(this, detalheA);
- if (detalheA.itemTransacaoAgrupado) {
- detalheA.itemTransacaoAgrupado = new ItemTransacaoAgrupado(detalheA.itemTransacaoAgrupado);
- }
- if (detalheA.headerLote) {
- detalheA.headerLote = new HeaderLote(detalheA.headerLote);
+ Object.assign(this, detalheA);
+ if (detalheA.ordemPagamentoAgrupadoHistorico) {
+ detalheA.ordemPagamentoAgrupadoHistorico = new OrdemPagamentoAgrupadoHistorico(detalheA.ordemPagamentoAgrupadoHistorico);
}
}
}
@@ -29,7 +28,7 @@ export class DetalheA extends EntityHelper {
@PrimaryGeneratedColumn({ primaryKeyConstraintName: 'PK_DetalheA_id' })
id: number;
- @ManyToOne(() => HeaderLote, { eager: true })
+ @ManyToOne(() => HeaderLote, { eager: false })
@JoinColumn({ foreignKeyConstraintName: 'FK_DetalheA_headerLote_ManyToOne' })
headerLote: HeaderLote;
@@ -117,12 +116,18 @@ export class DetalheA extends EntityHelper {
nsr: number;
/** `UQ_DetalheA_itemTransacaoAgrupado` */
- @OneToOne(() => ItemTransacaoAgrupado, { eager: true, nullable: false })
+ @OneToOne(() => ItemTransacaoAgrupado, { eager: false, nullable: true })
@JoinColumn({
foreignKeyConstraintName: 'FK_DetalheA_itemTransacaoAgrupado_OneToOne',
})
itemTransacaoAgrupado: ItemTransacaoAgrupado;
+ @OneToOne(() => OrdemPagamentoAgrupadoHistorico, { eager: true, nullable: true })
+ @JoinColumn({
+ foreignKeyConstraintName: 'FK_DetalheA_OrdemPagamentoAgrupadoHistorico_OneToOne',
+ })
+ ordemPagamentoAgrupadoHistorico: OrdemPagamentoAgrupadoHistorico | Nullable;
+
/** Nome do retorno mais recente lido, para referência. */
@Column({ type: String, unique: false, nullable: true })
retornoName: string | null;
diff --git a/src/cnab/entity/pagamento/detalhe-b.entity.ts b/src/cnab/entity/pagamento/detalhe-b.entity.ts
index d9609ce7d..2d82fbfbd 100644
--- a/src/cnab/entity/pagamento/detalhe-b.entity.ts
+++ b/src/cnab/entity/pagamento/detalhe-b.entity.ts
@@ -18,7 +18,7 @@ export class DetalheB extends EntityHelper {
@PrimaryGeneratedColumn({ primaryKeyConstraintName: 'PK_DetalheB_id' })
id: number;
- @OneToOne(() => DetalheA, { eager: false })
+ @OneToOne(() => DetalheA, { eager: true })
@JoinColumn({ foreignKeyConstraintName: 'FK_DetalheB_detalheA_OneToOne' })
detalheA: DetalheA;
diff --git a/src/cnab/entity/pagamento/header-arquivo.entity.ts b/src/cnab/entity/pagamento/header-arquivo.entity.ts
index 175732b5d..ceb7b4259 100644
--- a/src/cnab/entity/pagamento/header-arquivo.entity.ts
+++ b/src/cnab/entity/pagamento/header-arquivo.entity.ts
@@ -1,9 +1,10 @@
import { EntityHelper } from 'src/utils/entity-helper';
import { asStringOrDateTime } from 'src/utils/pipe-utils';
-import { AfterLoad, BeforeInsert, Column, CreateDateColumn, DeepPartial, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
+import { AfterLoad, BeforeInsert, Column, CreateDateColumn, DeepPartial, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
import { TransacaoAgrupado } from './transacao-agrupado.entity';
import { Cnab104AmbienteCliente } from 'src/cnab/enums/104/cnab-104-ambiente-cliente.enum';
import { HeaderArquivoStatus } from 'src/cnab/enums/pagamento/header-arquivo-status.enum';
+import { HeaderLote } from './header-lote.entity';
/**
* Pagamento.HeaderArquivo
@@ -87,6 +88,14 @@ export class HeaderArquivo extends EntityHelper {
@UpdateDateColumn()
updatedAt: Date;
+ @OneToMany(() => HeaderLote, (headerLote) => headerLote.headerArquivo, {
+ eager: false,
+ })
+ @JoinColumn({
+ foreignKeyConstraintName: 'FK_HeaderArquivo_headerLote_OneToMany',
+ })
+ headersLote: HeaderLote[];
+
@BeforeInsert()
setLoadValues() {
if (typeof this.codigoBanco === 'string') {
diff --git a/src/cnab/entity/pagamento/header-lote.entity.ts b/src/cnab/entity/pagamento/header-lote.entity.ts
index 4a7966b5e..402dbb6ca 100644
--- a/src/cnab/entity/pagamento/header-lote.entity.ts
+++ b/src/cnab/entity/pagamento/header-lote.entity.ts
@@ -1,8 +1,9 @@
import { EntityHelper } from 'src/utils/entity-helper';
-import { Column, CreateDateColumn, DeepPartial, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
+import { Column, CreateDateColumn, DeepPartial, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { HeaderArquivo } from './header-arquivo.entity';
import { Pagador } from './pagador.entity';
import { Cnab104CodigoCompromisso } from 'src/cnab/enums/104/cnab-104-codigo-compromisso.enum';
+import { DetalheA } from './detalhe-a.entity';
/**
* Pagamento.HeaderLote
@@ -58,10 +59,18 @@ export class HeaderLote extends EntityHelper {
@Column({ type: String, unique: false, nullable: true })
formaLancamento: string;
- @ManyToOne(() => Pagador, { eager: true })
+ @ManyToOne(() => Pagador, { eager: false })
@JoinColumn({ foreignKeyConstraintName: 'FK_HeaderLote_pagador_ManyToOne' })
pagador: Pagador;
+ @OneToMany(() => DetalheA, (detalheA) => detalheA.headerLote, {
+ eager: true,
+ })
+ @JoinColumn({
+ foreignKeyConstraintName: 'FK_HeaderLote_detalheA_OneToMany',
+ })
+ detalhesA: DetalheA[];
+
@CreateDateColumn()
createdAt: Date;
diff --git a/src/cnab/entity/pagamento/transacao-agrupado.entity.ts b/src/cnab/entity/pagamento/transacao-agrupado.entity.ts
index 433c66124..bc134b0b0 100644
--- a/src/cnab/entity/pagamento/transacao-agrupado.entity.ts
+++ b/src/cnab/entity/pagamento/transacao-agrupado.entity.ts
@@ -1,8 +1,6 @@
-import { nextFriday, startOfDay } from 'date-fns';
import { OrdemPagamentoDto } from 'src/cnab/dto/pagamento/ordem-pagamento.dto';
import { TransacaoStatusEnum } from 'src/cnab/enums/pagamento/transacao-status.enum';
import { Lancamento } from 'src/lancamento/entities/lancamento.entity';
-import { yearMonthDayToDate } from 'src/utils/date-utils';
import { EntityHelper } from 'src/utils/entity-helper';
import { Column, CreateDateColumn, DeepPartial, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
import { ItemTransacaoAgrupado } from './item-transacao-agrupado.entity';
diff --git a/src/cnab/enums/novo-remessa/status-remessa.enum.ts b/src/cnab/enums/novo-remessa/status-remessa.enum.ts
new file mode 100644
index 000000000..f92ff36af
--- /dev/null
+++ b/src/cnab/enums/novo-remessa/status-remessa.enum.ts
@@ -0,0 +1,13 @@
+export enum StatusRemessaEnum {
+ Criado = 0,//Criado no CCT
+ PreparadoParaEnvio = 1,//Remessa enviado
+ AguardandoPagamento = 2, //Aguardando Pagamento
+ Efetivado = 3,//Segundo retorno
+ NaoEfetivado = 4 //Retorno com Erro
+}
+
+
+export function getStatusRemessaEnumByValue(value: StatusRemessaEnum): string | undefined {
+ return Object.keys(StatusRemessaEnum)
+ .find(key => StatusRemessaEnum[key as keyof typeof StatusRemessaEnum] === value);
+}
\ No newline at end of file
diff --git a/src/cnab/enums/ocorrencia.enum.ts b/src/cnab/enums/ocorrencia.enum.ts
index d61c669cc..bda4a21e8 100644
--- a/src/cnab/enums/ocorrencia.enum.ts
+++ b/src/cnab/enums/ocorrencia.enum.ts
@@ -96,3 +96,8 @@ export enum OcorrenciaEnum {
/** Ocorrências X são erros customizados do CCT. Não existem no CNAB */
' ' = 'Ocorreu um erro! Por favor, aguarde a liberação do pagamento.',
}
+
+export function getDescricaoOcorrenciaEnumByValue(value: OcorrenciaEnum): string | undefined {
+ return Object.keys(OcorrenciaEnum)
+ .find(key => OcorrenciaEnum[key as keyof typeof OcorrenciaEnum] === value);
+}
\ No newline at end of file
diff --git a/src/cnab/enums/pagamento/header-arquivo-status.enum.ts b/src/cnab/enums/pagamento/header-arquivo-status.enum.ts
index f08766075..f5bbf10ae 100644
--- a/src/cnab/enums/pagamento/header-arquivo-status.enum.ts
+++ b/src/cnab/enums/pagamento/header-arquivo-status.enum.ts
@@ -4,3 +4,9 @@ export enum HeaderArquivoStatus {
_2_remessaGerado = 'remessaGerado',
_3_remessaEnviado = 'remessaEnviado',
}
+
+export enum HeaderName{
+ CONSORCIO = 'CONSORCIO',
+ MODAL = 'MODAL',
+ VLT = 'VLT',
+}
diff --git a/src/cnab/interfaces/cnab-240/104/pagamento/cnab-file-104-pgto.interface.ts b/src/cnab/interfaces/cnab-240/104/pagamento/cnab-file-104-pgto.interface.ts
index f4a30d55e..15100b6b7 100644
--- a/src/cnab/interfaces/cnab-240/104/pagamento/cnab-file-104-pgto.interface.ts
+++ b/src/cnab/interfaces/cnab-240/104/pagamento/cnab-file-104-pgto.interface.ts
@@ -27,7 +27,7 @@ export class CnabFile104PgtoDTO implements CnabFile104Pgto {
trailerArquivo: CnabTrailerArquivo104;
static fromDTO(args: {
- headerArquivoDTO: HeaderArquivoDTO | HeaderArquivo; //
+ headerArquivoDTO: HeaderArquivoDTO;
headerLoteDTOs: HeaderLoteDTO[];
isCancelamento?: boolean;
isTeste?: boolean;
diff --git a/src/cnab/interfaces/cnab-240/104/pagamento/cnab-header-lote-104-pgto.interface.ts b/src/cnab/interfaces/cnab-240/104/pagamento/cnab-header-lote-104-pgto.interface.ts
index 31fc7c63c..cb823b85b 100644
--- a/src/cnab/interfaces/cnab-240/104/pagamento/cnab-header-lote-104-pgto.interface.ts
+++ b/src/cnab/interfaces/cnab-240/104/pagamento/cnab-header-lote-104-pgto.interface.ts
@@ -1,6 +1,8 @@
import { HeaderLoteDTO } from 'src/cnab/dto/pagamento/header-lote.dto';
import { HeaderArquivo } from 'src/cnab/entity/pagamento/header-arquivo.entity';
+import { HeaderLote } from 'src/cnab/entity/pagamento/header-lote.entity';
import { Pagador } from 'src/cnab/entity/pagamento/pagador.entity';
+import { CnabTipoInscricao } from 'src/cnab/enums/all/cnab-tipo-inscricao.enum';
import { CnabField, CnabFieldAs } from 'src/cnab/interfaces/cnab-all/cnab-field.interface';
import { Cnab104PgtoTemplates } from 'src/cnab/templates/cnab-240/104/pagamento/cnab-104-pgto-templates.const';
import { DeepPartial } from 'typeorm';
@@ -57,7 +59,7 @@ export class CnabHeaderLote104PgtoDTO {
constructor(dto: CnabHeaderLote104Pgto) {
Object.assign(this, dto);
}
- static fromDTO(headerLoteDTO: HeaderLoteDTO): CnabHeaderLote104Pgto {
+ static fromDTO(headerLoteDTO: HeaderLoteDTO | HeaderLote): CnabHeaderLote104Pgto {
const headerLote104 = new CnabHeaderLote104PgtoDTO(structuredClone(Cnab104PgtoTemplates.file104.registros.headerLote));
const headerArquivo = headerLoteDTO.headerArquivo as HeaderArquivo;
const pagador = headerLoteDTO.pagador as DeepPartial;
@@ -84,6 +86,25 @@ export class CnabHeaderLote104PgtoDTO {
return headerLote104;
}
+
+ static convert(headerLoteDTO: HeaderLote, headerArquivo: HeaderArquivo): CnabHeaderLote104Pgto {
+ const headerLote104 = new CnabHeaderLote104PgtoDTO(structuredClone(Cnab104PgtoTemplates.file104.registros.headerLote));
+ headerLote104.codigoConvenioBanco.value = headerLoteDTO.codigoConvenioBanco;
+ headerLote104.numeroInscricao.value = headerLoteDTO.numeroInscricao;
+ headerLote104.parametroTransmissao.value = headerLoteDTO.parametroTransmissao;
+ headerLote104.tipoInscricao.value = headerLoteDTO.tipoInscricao ==='cpf'?CnabTipoInscricao.CPF:CnabTipoInscricao.CNPJ;
+ headerLote104.formaLancamento.value = headerLoteDTO.formaLancamento;
+ headerLote104.codigoCompromisso.value = headerLoteDTO.codigoCompromisso;
+ // Pagador
+ headerLote104.agenciaContaCorrente.value = headerArquivo.agencia;
+ headerLote104.dvAgencia.value = headerArquivo.dvAgencia;
+ headerLote104.numeroConta.value = headerArquivo.numeroConta;
+ headerLote104.dvConta.value = headerArquivo.dvConta;
+ headerLote104.nomeEmpresa.value = headerArquivo.nomeEmpresa;
+
+ return headerLote104;
+ }
+
codigoBanco: CnabField;
loteServico: CnabField;
codigoRegistro: CnabFieldAs;
diff --git a/src/cnab/novo-remessa/controller/ordem-pagamento.controller.ts b/src/cnab/novo-remessa/controller/ordem-pagamento.controller.ts
new file mode 100644
index 000000000..e3b309b0b
--- /dev/null
+++ b/src/cnab/novo-remessa/controller/ordem-pagamento.controller.ts
@@ -0,0 +1,167 @@
+import {
+ Controller,
+ Get,
+ HttpCode,
+ HttpStatus,
+ Param, ParseArrayPipe,
+ Query,
+ Request,
+ SerializeOptions,
+ UseGuards,
+} from '@nestjs/common';
+import { AuthGuard } from '@nestjs/passport';
+import { ApiBearerAuth, ApiParam, ApiQuery, ApiTags } from '@nestjs/swagger';
+import { CommonApiParams } from 'src/utils/api-param/common-api-params';
+import { DateApiParams } from 'src/utils/api-param/date-api-param';
+import { CustomLogger } from 'src/utils/custom-logger';
+import { IRequest } from 'src/utils/interfaces/request.interface';
+import { ParseNumberPipe } from 'src/utils/pipes/parse-number.pipe';
+import { DateQueryParams } from 'src/utils/query-param/date.query-param';
+import { canProceed, getRequestLog } from 'src/utils/request-utils';
+import { OrdemPagamentoService } from '../service/ordem-pagamento.service';
+import { OrdemPagamentoSemanalDto } from '../dto/ordem-pagamento-semanal.dto';
+import { BigqueryTransacaoService } from '../../../bigquery/services/bigquery-transacao.service';
+import { BigqueryTransacao } from '../../../bigquery/entities/transacao.bigquery-entity';
+import { OrdemPagamentoMensalDto } from '../dto/ordem-pagamento-mensal.dto';
+import { OrdemPagamentoPendenteNuncaRemetidasDto } from '../dto/ordem-pagamento-pendente-nunca-remetidas.dto';
+import { UsersService } from '../../../users/users.service';
+
+@ApiTags('OrdemPagamento')
+@Controller({
+ path: 'ordem-pagamento',
+ version: '1',
+})
+export class OrdemPagamentoController {
+ private logger = new CustomLogger(OrdemPagamentoController.name, {
+ timestamp: true,
+ });
+
+ constructor(private readonly ordemPagamentoService: OrdemPagamentoService,
+ private readonly bigqueryTransacaoService: BigqueryTransacaoService,
+ private readonly usersService: UsersService) {}
+
+ @Get('mensal')
+ @UseGuards(AuthGuard('jwt'))
+ @SerializeOptions({ groups: ['me'] })
+ @ApiBearerAuth()
+ @ApiQuery(DateApiParams.yearMonth)
+ @ApiQuery(CommonApiParams.userId)
+ @HttpCode(HttpStatus.OK)
+ async get(
+ @Request() request: IRequest, //
+ @Query(...DateQueryParams.yearMonth) yearMonth: string,
+ @Query('userId', new ParseNumberPipe({ min: 1, optional: true })) userId?: number | null,
+ ): Promise {
+ this.logger.log(getRequestLog(request));
+ const isUserIdNumber = userId !== null && !isNaN(Number(userId));
+ const yearMonthDate = yearMonth ? new Date(yearMonth): new Date();
+ const userIdNum = isUserIdNumber ? Number(userId) : request.user.id;
+ canProceed(request, Number(userId));
+ return this.ordemPagamentoService.findOrdensPagamentoAgrupadasPorMes(userIdNum, yearMonthDate);
+ }
+
+
+ @Get('semanal/:ordemPagamentoAgrupadoId')
+ @UseGuards(AuthGuard('jwt'))
+ @SerializeOptions({ groups: ['me'] })
+ @ApiBearerAuth()
+ @ApiParam(CommonApiParams.ordemPagamentoAgrupadoId)
+ @ApiQuery(CommonApiParams.userId)
+ @HttpCode(HttpStatus.OK)
+ async getSemanal(
+ @Request() request: IRequest, //
+ @Param('ordemPagamentoAgrupadoId', new ParseNumberPipe({ min: 1, optional: false })) ordemPagamentoAgrupadoId: number,
+ @Query('userId', new ParseNumberPipe({ min: 1, optional: false })) userId: number | null,
+ ): Promise {
+ this.logger.log(getRequestLog(request));
+ const isUserIdNumber = userId !== null && !isNaN(Number(userId));
+ const userIdNum = isUserIdNumber ? Number(userId) : request.user.id;
+ canProceed(request, Number(userId));
+ return this.ordemPagamentoService.findOrdensPagamentoAgrupadasByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId, userIdNum);
+ }
+
+
+ @Get('diario/:ordemPagamentoId')
+ @UseGuards(AuthGuard('jwt'))
+ @SerializeOptions({ groups: ['me'] })
+ @ApiBearerAuth()
+ @ApiParam(CommonApiParams.ordemPagamentoId)
+ @ApiQuery(CommonApiParams.userId)
+ @HttpCode(HttpStatus.OK)
+ async getDiario(
+ @Request() request: IRequest, //
+ @Param('ordemPagamentoId', new ParseNumberPipe({ min: 1, optional: false })) ordemPagamentoId: number,
+ @Query('userId', new ParseNumberPipe({ min: 1, optional: false })) userId: number | null,
+ ): Promise {
+ this.logger.log(getRequestLog(request));
+ const isUserIdNumber = userId !== null && !isNaN(Number(userId));
+ const userIdNum = isUserIdNumber ? Number(userId) : request.user.id;
+ const user = await this.usersService.findOne({ id: userIdNum})
+ canProceed(request, Number(userId));
+ return this.bigqueryTransacaoService.findByOrdemPagamentoIdIn([ordemPagamentoId], user?.cpfCnpj, request);
+ }
+
+ @Get('diario')
+ @UseGuards(AuthGuard('jwt'))
+ @SerializeOptions({ groups: ['me'] })
+ @ApiBearerAuth()
+ @ApiQuery({ name: 'ordemPagamentoIds', type: [Number], required: true })
+ @ApiQuery(CommonApiParams.userId)
+ @HttpCode(HttpStatus.OK)
+ async getDiarioParaVariasOrdens(
+ @Request() request: IRequest, //
+ @Query('ordemPagamentoIds', new ParseArrayPipe()) ordemPagamentoIds: number[],
+ @Query('userId', new ParseNumberPipe({ min: 1, optional: false })) userId: number | null,
+ ): Promise {
+ this.logger.log(getRequestLog(request));
+ const isUserIdNumber = userId !== null && !isNaN(Number(userId));
+ const userIdNum = isUserIdNumber ? Number(userId) : request.user.id;
+ const user = await this.usersService.findOne({ id: userIdNum });
+ canProceed(request, Number(userId));
+ return this.bigqueryTransacaoService.findByOrdemPagamentoIdIn(ordemPagamentoIds, user?.cpfCnpj, request);
+ }
+
+
+ @Get('transacoes-semana/:ordemPagamentoAgrupadoId')
+ @UseGuards(AuthGuard('jwt'))
+ @SerializeOptions({ groups: ['me'] })
+ @ApiBearerAuth()
+ @ApiParam(CommonApiParams.ordemPagamentoAgrupadoId)
+ @ApiQuery(CommonApiParams.userId)
+ @HttpCode(HttpStatus.OK)
+ async getTransacoesSemana(
+ @Request() request: IRequest, //
+ @Param('ordemPagamentoAgrupadoId', new ParseNumberPipe({ min: 1, optional: false })) ordemPagamentoAgrupadoId: number,
+ @Query('userId', new ParseNumberPipe({ min: 1, optional: false })) userId: number | null,
+ ): Promise {
+ this.logger.log(getRequestLog(request));
+ const isUserIdNumber = userId !== null && !isNaN(Number(userId));
+ const userIdNum = isUserIdNumber ? Number(userId) : request.user.id;
+ canProceed(request, Number(userId));
+
+ const ordensPagamento = await this.ordemPagamentoService.findOrdensPagamentoByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId, userIdNum);
+ const ordemPagamentoIds = ordensPagamento.map((ordem) => ordem.ordemId);
+
+ const user = await this.usersService.findOne({ id: userIdNum})
+ return this.bigqueryTransacaoService.findManyByOrdemPagamentoIdInGroupedByTipoTransacao(ordemPagamentoIds, user?.cpfCnpj, request);
+ }
+
+ @Get('transacoes-dias-anteriores/:ordemPagamentoAgrupadoId')
+ @UseGuards(AuthGuard('jwt'))
+ @SerializeOptions({ groups: ['me'] })
+ @ApiBearerAuth()
+ @HttpCode(HttpStatus.OK)
+ @ApiQuery(CommonApiParams.userId)
+ async getTransacoesDiasAnteriores(
+ @Request() request: IRequest, //
+ @Param('ordemPagamentoAgrupadoId', new ParseNumberPipe({ min: 1, optional: false })) ordemPagamentoAgrupadoId: number,
+ @Query('userId', new ParseNumberPipe({ min: 1, optional: false })) userId: number | null,
+ ): Promise {
+ this.logger.log(getRequestLog(request));
+ const isUserIdNumber = userId !== null && !isNaN(Number(userId));
+ const userIdNum = isUserIdNumber ? Number(userId) : request.user.id;
+ canProceed(request, Number(userId));
+ return this.ordemPagamentoService.findOrdensPagamentoDiasAnterioresByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId, userIdNum);
+ }
+
+}
diff --git a/src/cnab/novo-remessa/convertTo/bigquery-to-ordem-pagamento.convert.ts b/src/cnab/novo-remessa/convertTo/bigquery-to-ordem-pagamento.convert.ts
new file mode 100644
index 000000000..f6771d380
--- /dev/null
+++ b/src/cnab/novo-remessa/convertTo/bigquery-to-ordem-pagamento.convert.ts
@@ -0,0 +1,32 @@
+import { Injectable } from "@nestjs/common";
+import { BigqueryOrdemPagamentoDTO } from "src/bigquery/dtos/bigquery-ordem-pagamento.dto";
+import { CustomLogger } from "src/utils/custom-logger";
+import { OrdemPagamento } from "../entity/ordem-pagamento.entity";
+
+
+@Injectable()
+export class BigQueryToOrdemPagamento {
+
+ static logger = new CustomLogger(BigQueryToOrdemPagamento.name, { timestamp: true });
+
+ constructor() { }
+
+ static convert(ordem: BigqueryOrdemPagamentoDTO, userId: number | undefined) {
+ const METHOD = 'convert';
+ this.logger.debug(`Sincronizado ${ordem.idOrdemPagamento} `, METHOD);
+ var result = new OrdemPagamento();
+ result.id = ordem.id;
+ result.dataOrdem = new Date(ordem.dataOrdem);
+ result.idConsorcio = ordem.idConsorcio;
+ result.idOperadora = ordem.idOperadora;
+ result.operadoraCpfCnpj = ordem.operadoraCpfCnpj;
+ result.idOrdemPagamento = ordem.idOrdemPagamento;
+ result.nomeConsorcio = ordem.consorcio;
+ result.nomeOperadora = ordem.operadora;
+ result.userId = userId;
+ result.valor = ordem.valorTotalTransacaoLiquido;
+ result.bqUpdatedAt = new Date(ordem.datetimeUltimaAtualizacao);
+ result.dataCaptura = ordem.dataCaptura;
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/convertTo/detalhe-a-to-detalhe-b.convert.ts b/src/cnab/novo-remessa/convertTo/detalhe-a-to-detalhe-b.convert.ts
new file mode 100644
index 000000000..82941d7bf
--- /dev/null
+++ b/src/cnab/novo-remessa/convertTo/detalhe-a-to-detalhe-b.convert.ts
@@ -0,0 +1,24 @@
+import { Injectable } from "@nestjs/common";
+import { CustomLogger } from "src/utils/custom-logger";
+import { DetalheA } from "src/cnab/entity/pagamento/detalhe-a.entity";
+import { HeaderLote } from "src/cnab/entity/pagamento/header-lote.entity";
+import { OrdemPagamentoAgrupado } from "../entity/ordem-pagamento-agrupado.entity";
+import { DetalheB } from "src/cnab/entity/pagamento/detalhe-b.entity";
+
+
+@Injectable()
+export class DetalheAToDetalheB {
+
+ static logger = new CustomLogger(DetalheAToDetalheB.name, { timestamp: true });
+
+ constructor() { }
+
+ static convert(detalheA: DetalheA,ordem: OrdemPagamentoAgrupado){
+ const db = new DetalheB();
+ db.detalheA = detalheA;
+ db.nsr = detalheA.nsr+1;
+ db.dataVencimento = ordem.dataPagamento
+ db.createdAt = new Date();
+ return db;
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/convertTo/detalhes-to-cnab.convert.ts b/src/cnab/novo-remessa/convertTo/detalhes-to-cnab.convert.ts
new file mode 100644
index 000000000..38e2df0b8
--- /dev/null
+++ b/src/cnab/novo-remessa/convertTo/detalhes-to-cnab.convert.ts
@@ -0,0 +1,54 @@
+import { Injectable } from "@nestjs/common";
+import { DetalheA } from "src/cnab/entity/pagamento/detalhe-a.entity";
+import { DetalheB } from "src/cnab/entity/pagamento/detalhe-b.entity";
+import { CnabDetalheA_104 } from "src/cnab/interfaces/cnab-240/104/pagamento/cnab-detalhe-a-104.interface";
+import { CnabDetalheB_104 } from "src/cnab/interfaces/cnab-240/104/pagamento/cnab-detalhe-b-104.interface";
+import { Cnab104PgtoTemplates } from "src/cnab/templates/cnab-240/104/pagamento/cnab-104-pgto-templates.const";
+import { UsersService } from "src/users/users.service";
+import { isCpfOrCnpj } from "src/utils/cpf-cnpj";
+import { CustomLogger } from "src/utils/custom-logger";
+import { OrdemPagamentoAgrupadoHistorico } from "../entity/ordem-pagamento-agrupado-historico.entity";
+import { OrdemPagamentoAgrupadoHistoricoDTO } from "../dto/ordem-pagamento-agrupado-historico.dto";
+import { CnabTipoInscricao } from "src/cnab/enums/all/cnab-tipo-inscricao.enum";
+
+const sc = structuredClone;
+const PgtoRegistros = Cnab104PgtoTemplates.file104.registros;
+
+@Injectable()
+export class DetalhesToCnab {
+
+ static logger = new CustomLogger(DetalhesToCnab.name, { timestamp: true });
+
+ static userService: UsersService;
+
+ constructor() { }
+
+ static async convert(detalheA: DetalheA, detalheB: DetalheB,
+ historico: OrdemPagamentoAgrupadoHistoricoDTO) {
+ const detalheACnab: CnabDetalheA_104 = sc(PgtoRegistros.detalheA);
+ detalheACnab.codigoBancoDestino.value = historico?.userBankCode;
+ detalheACnab.codigoAgenciaDestino.value = historico?.userBankAgency.substring(0, historico.userBankAgency.length - 1); ;
+ detalheACnab.dvAgenciaDestino.value = historico?.userBankAgency.substring(historico.userBankAgency.length - 1);
+ detalheACnab.contaCorrenteDestino.value = historico?.userBankAccount;
+ detalheACnab.dvContaDestino.value = historico?.userBankAccountDigit;
+ detalheACnab.nomeTerceiro.value = historico.username;
+ detalheACnab.numeroDocumentoEmpresa.value = detalheA.numeroDocumentoEmpresa;
+ detalheACnab.dataVencimento.value = detalheA.dataVencimento;
+ detalheACnab.valorLancamento.value = Number(detalheA.valorLancamento);
+ detalheACnab.valorRealEfetivado.value = detalheA.valorRealEfetivado;
+ detalheACnab.nsr.value = detalheA.nsr;
+
+
+ // DetalheB
+ const detalheBCnab: CnabDetalheB_104 = sc(PgtoRegistros.detalheB);
+ detalheBCnab.tipoInscricao.value = isCpfOrCnpj(historico?.usercpfcnpj) ==='cpf'?CnabTipoInscricao.CPF:CnabTipoInscricao.CNPJ;
+ detalheBCnab.numeroInscricao.value = historico?.usercpfcnpj;
+ detalheBCnab.dataVencimento.value = detalheA.dataVencimento;
+ detalheBCnab.nsr.value = detalheB.nsr;
+
+ return {
+ detalheA: detalheACnab,
+ detalheB: detalheBCnab,
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/convertTo/header-arquivo-to-cnabfile.convert.ts b/src/cnab/novo-remessa/convertTo/header-arquivo-to-cnabfile.convert.ts
new file mode 100644
index 000000000..3f736a339
--- /dev/null
+++ b/src/cnab/novo-remessa/convertTo/header-arquivo-to-cnabfile.convert.ts
@@ -0,0 +1,34 @@
+import { Injectable } from "@nestjs/common";
+import { CnabHeaderArquivo104 } from "src/cnab/dto/cnab-240/104/cnab-header-arquivo-104.dto";
+import { HeaderArquivo } from "src/cnab/entity/pagamento/header-arquivo.entity";
+import { Cnab104AmbienteCliente } from "src/cnab/enums/104/cnab-104-ambiente-cliente.enum";
+import { Cnab104PgtoTemplates } from "src/cnab/templates/cnab-240/104/pagamento/cnab-104-pgto-templates.const";
+import { CustomLogger } from "src/utils/custom-logger";
+
+
+@Injectable()
+export class HeaderArquivoToCnabFile {
+
+ static logger = new CustomLogger(HeaderArquivoToCnabFile.name, { timestamp: true });
+
+ constructor() { }
+
+ static convert(headerArquivo: HeaderArquivo):CnabHeaderArquivo104{
+ const headerArquivo104: CnabHeaderArquivo104 = structuredClone(Cnab104PgtoTemplates.file104.registros.headerArquivo);
+ headerArquivo104.codigoBanco.value = headerArquivo.codigoBanco;
+ headerArquivo104.numeroInscricao.value = headerArquivo.numeroInscricao;
+ headerArquivo104.codigoConvenioBanco.value = headerArquivo.codigoConvenio;
+ headerArquivo104.parametroTransmissao.value = headerArquivo.parametroTransmissao;
+ headerArquivo104.agenciaContaCorrente.value = headerArquivo.agencia;
+ headerArquivo104.numeroConta.value = headerArquivo.numeroConta;
+ headerArquivo104.dvAgencia.value = headerArquivo.dvAgencia;
+ headerArquivo104.dvConta.value = headerArquivo.dvConta;
+ headerArquivo104.nomeEmpresa.value = headerArquivo.nomeEmpresa;
+ headerArquivo104.tipoArquivo.value = headerArquivo.tipoArquivo;
+ headerArquivo104.dataGeracaoArquivo.value = headerArquivo.dataGeracao;
+ headerArquivo104.horaGeracaoArquivo.value = headerArquivo.horaGeracao;
+ headerArquivo104.nsa.value = headerArquivo.nsa;
+ headerArquivo104.ambienteCliente.value = Cnab104AmbienteCliente.Producao;
+ return headerArquivo104;
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/convertTo/header-lote-to-detalhe-a.convert.ts b/src/cnab/novo-remessa/convertTo/header-lote-to-detalhe-a.convert.ts
new file mode 100644
index 000000000..c3e8880d8
--- /dev/null
+++ b/src/cnab/novo-remessa/convertTo/header-lote-to-detalhe-a.convert.ts
@@ -0,0 +1,41 @@
+import { Injectable } from "@nestjs/common";
+import { CustomLogger } from "src/utils/custom-logger";
+import { HeaderLote } from "src/cnab/entity/pagamento/header-lote.entity";
+import { OrdemPagamentoAgrupado } from "../entity/ordem-pagamento-agrupado.entity";
+import { OrdemPagamentoAgrupadoHistorico } from "../entity/ordem-pagamento-agrupado-historico.entity";
+import { DetalheADTO } from "src/cnab/dto/pagamento/detalhe-a.dto";
+import { Cnab104FormaParcelamento } from "src/cnab/enums/104/cnab-104-forma-parcelamento.enum";
+
+
+@Injectable()
+export class HeaderLoteToDetalheA {
+
+ static logger = new CustomLogger(HeaderLoteToDetalheA.name, { timestamp: true });
+
+ constructor() { }
+
+ static async convert(headerLote: HeaderLote,ordem: OrdemPagamentoAgrupado,nsr?: number,
+ hist?:OrdemPagamentoAgrupadoHistorico,numeroDocumento?: number){
+
+ const da = new DetalheADTO();
+ da.headerLote = headerLote;
+ da.nsr = nsr;
+ da.dataVencimento = hist?.dataReferencia ?? ordem.dataPagamento;
+ da.periodoVencimento = ordem.dataPagamento;
+ da.valorLancamento = ordem.valorTotal;
+ da.valorRealEfetivado = ordem.valorTotal;
+ da.numeroDocumentoEmpresa = numeroDocumento;
+ da.indicadorBloqueio = 'N';
+ da.quantidadeMoeda = 0;
+ da.indicadorFormaParcelamento = Cnab104FormaParcelamento.DataFixa.toString();
+ da.finalidadeDOC = "00";
+ da.loteServico = headerLote.formaLancamento === '41'?1:2;
+ da.tipoMoeda ='BRL';
+ da.numeroDocumentoBanco = "0";
+ da.numeroParcela = 1;
+ da.quantidadeParcelas = 1;
+ if(hist)
+ da.ordemPagamentoAgrupadoHistorico = hist;
+ return da;
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/convertTo/ordem-pagamento-agrupado-to-header-arquivo.convert.ts b/src/cnab/novo-remessa/convertTo/ordem-pagamento-agrupado-to-header-arquivo.convert.ts
new file mode 100644
index 000000000..7e24e8c4f
--- /dev/null
+++ b/src/cnab/novo-remessa/convertTo/ordem-pagamento-agrupado-to-header-arquivo.convert.ts
@@ -0,0 +1,41 @@
+import { Injectable } from "@nestjs/common";
+import { CustomLogger } from "src/utils/custom-logger";
+import { Pagador } from "src/cnab/entity/pagamento/pagador.entity";
+import { Cnab104AmbienteCliente } from "src/cnab/enums/104/cnab-104-ambiente-cliente.enum";
+import { HeaderArquivoDTO } from "src/cnab/dto/pagamento/header-arquivo.dto";
+import { Cnab104PgtoTemplates } from "src/cnab/templates/cnab-240/104/pagamento/cnab-104-pgto-templates.const";
+import { HeaderArquivoTipoArquivo } from "src/cnab/enums/pagamento/header-arquivo-tipo-arquivo.enum";
+import { HeaderArquivoStatus, HeaderName } from "src/cnab/enums/pagamento/header-arquivo-status.enum";
+
+
+@Injectable()
+export class OrdemPagamentoAgrupadoToHeaderArquivo {
+
+ static logger = new CustomLogger(OrdemPagamentoAgrupadoToHeaderArquivo.name, { timestamp: true });
+
+ constructor() { }
+
+ static convert(pagador: Pagador, nsa: number,headerName: HeaderName) {
+ const now = new Date();
+ return new HeaderArquivoDTO({
+ _isConf: false,
+ agencia: pagador.agencia,
+ codigoBanco: Cnab104PgtoTemplates.file104.registros.headerArquivo.codigoBanco.value,
+ tipoInscricao: Cnab104PgtoTemplates.file104.registros.headerArquivo.tipoInscricao.value,
+ numeroInscricao: String(pagador.cpfCnpj),
+ codigoConvenio: Cnab104PgtoTemplates.file104.registros.headerArquivo.codigoConvenioBanco.value,
+ parametroTransmissao: Cnab104PgtoTemplates.file104.registros.headerArquivo.parametroTransmissao.value,
+ dataGeracao: now,
+ horaGeracao: now,
+ dvAgencia: pagador.dvAgencia,
+ dvConta: pagador.dvConta,
+ nomeEmpresa: pagador.nomeEmpresa,
+ numeroConta: pagador.conta,
+ tipoArquivo: HeaderArquivoTipoArquivo.Remessa,
+ nsa:nsa,
+ status: HeaderArquivoStatus._2_remessaGerado,
+ remessaName: headerName,
+ ambienteCliente: Cnab104AmbienteCliente.Producao,
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/dto/ordem-pagamento-agrupado-historico.dto.ts b/src/cnab/novo-remessa/dto/ordem-pagamento-agrupado-historico.dto.ts
new file mode 100644
index 000000000..a7aa388e5
--- /dev/null
+++ b/src/cnab/novo-remessa/dto/ordem-pagamento-agrupado-historico.dto.ts
@@ -0,0 +1,38 @@
+import { EntityHelper } from 'src/utils/entity-helper';
+import { Column, DeepPartial, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
+import { StatusRemessaEnum } from 'src/cnab/enums/novo-remessa/status-remessa.enum';
+import { OcorrenciaEnum } from 'src/cnab/enums/ocorrencia.enum';
+import { OrdemPagamentoAgrupado } from '../entity/ordem-pagamento-agrupado.entity';
+
+@Entity()
+export class OrdemPagamentoAgrupadoHistoricoDTO{
+
+ constructor(dto?: DeepPartial) {
+ if (dto) {
+ Object.assign(this, dto);
+ }
+ }
+
+ id: number;
+
+ dataReferencia: Date;
+
+ username: string;
+
+ usercpfcnpj: string;
+
+ userBankCode: string;
+
+ userBankAgency: string;
+
+ userBankAccount: string;
+
+ userBankAccountDigit: string;
+
+ statusRemessa: StatusRemessaEnum;
+
+ motivoStatusRemessa: OcorrenciaEnum;
+
+ ordemPagamentoAgrupadoId: Number;
+
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/dto/ordem-pagamento-agrupado-mensal.dto.ts b/src/cnab/novo-remessa/dto/ordem-pagamento-agrupado-mensal.dto.ts
new file mode 100644
index 000000000..bccb2f680
--- /dev/null
+++ b/src/cnab/novo-remessa/dto/ordem-pagamento-agrupado-mensal.dto.ts
@@ -0,0 +1,9 @@
+export class OrdemPagamentoAgrupadoMensalDto {
+ ordemPagamentoAgrupadoId: number | undefined;
+ data: Date;
+ valorTotal: number | undefined;
+ statusRemessa: number | undefined;
+ motivoStatusRemessa: string | undefined;
+ descricaoStatusRemessa: string | undefined;
+ descricaoMotivoStatusRemessa: string | undefined;
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/dto/ordem-pagamento-mensal.dto.ts b/src/cnab/novo-remessa/dto/ordem-pagamento-mensal.dto.ts
new file mode 100644
index 000000000..2e0175310
--- /dev/null
+++ b/src/cnab/novo-remessa/dto/ordem-pagamento-mensal.dto.ts
@@ -0,0 +1,7 @@
+import { OrdemPagamentoAgrupadoMensalDto } from './ordem-pagamento-agrupado-mensal.dto';
+
+export class OrdemPagamentoMensalDto {
+ ordens: OrdemPagamentoAgrupadoMensalDto[];
+ valorTotal: number;
+ valorTotalPago: number;
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/dto/ordem-pagamento-pendente-nunca-remetidas.dto.ts b/src/cnab/novo-remessa/dto/ordem-pagamento-pendente-nunca-remetidas.dto.ts
new file mode 100644
index 000000000..0159ad1e5
--- /dev/null
+++ b/src/cnab/novo-remessa/dto/ordem-pagamento-pendente-nunca-remetidas.dto.ts
@@ -0,0 +1,6 @@
+export class OrdemPagamentoPendenteNuncaRemetidasDto {
+ id: number;
+ valor: number;
+ dataOrdem: Date;
+ userId: number;
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/dto/ordem-pagamento-pendente.dto.ts b/src/cnab/novo-remessa/dto/ordem-pagamento-pendente.dto.ts
new file mode 100644
index 000000000..afd322e7b
--- /dev/null
+++ b/src/cnab/novo-remessa/dto/ordem-pagamento-pendente.dto.ts
@@ -0,0 +1,12 @@
+export class OrdemPagamentoPendenteDto {
+ id: number;
+ valor: number;
+ dataPagamento: Date;
+ dataReferencia: Date;
+ statusRemessa: string;
+ motivoStatusRemessa: string;
+ ordemPagamentoAgrupadoId: number;
+ userId: number;
+ createdAt: Date;
+ updatedAt: Date;
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/dto/ordem-pagamento-semanal.dto.ts b/src/cnab/novo-remessa/dto/ordem-pagamento-semanal.dto.ts
new file mode 100644
index 000000000..235c8427a
--- /dev/null
+++ b/src/cnab/novo-remessa/dto/ordem-pagamento-semanal.dto.ts
@@ -0,0 +1,18 @@
+import { DeepPartial } from 'typeorm';
+
+export class OrdemPagamentoSemanalDto {
+ ordemId: number | undefined;
+ valor: number;
+ dataOrdem: Date;
+ dataReferencia: Date;
+ statusRemessa: number | undefined;
+ motivoStatusRemessa: string | undefined;
+ dataCaptura: Date | undefined;
+ ids: any[];
+
+ constructor(dto?: DeepPartial) {
+ if (dto) {
+ Object.assign(this, dto);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/dto/ordens-pagamento-agrupadas.dto.ts b/src/cnab/novo-remessa/dto/ordens-pagamento-agrupadas.dto.ts
new file mode 100644
index 000000000..ae9fa675f
--- /dev/null
+++ b/src/cnab/novo-remessa/dto/ordens-pagamento-agrupadas.dto.ts
@@ -0,0 +1,10 @@
+import { OrdemPagamento } from '../entity/ordem-pagamento.entity';
+
+interface OrdensPagamentoAgrupadasDto {
+ userId: number;
+ idOperadora: string;
+ valorTotal: number;
+ ordensPagamento: OrdemPagamento[];
+}
+
+export { OrdensPagamentoAgrupadasDto };
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/entity/ordem-pagamento-agrupado-historico.entity.ts b/src/cnab/novo-remessa/entity/ordem-pagamento-agrupado-historico.entity.ts
new file mode 100644
index 000000000..067dc4663
--- /dev/null
+++ b/src/cnab/novo-remessa/entity/ordem-pagamento-agrupado-historico.entity.ts
@@ -0,0 +1,44 @@
+import { EntityHelper } from 'src/utils/entity-helper';
+import { Column, DeepPartial, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
+import { OrdemPagamentoAgrupado } from './ordem-pagamento-agrupado.entity';
+import { StatusRemessaEnum } from 'src/cnab/enums/novo-remessa/status-remessa.enum';
+import { OcorrenciaEnum } from 'src/cnab/enums/ocorrencia.enum';
+
+@Entity()
+export class OrdemPagamentoAgrupadoHistorico extends EntityHelper {
+ constructor(dto?: DeepPartial) {
+ super();
+ if (dto) {
+ Object.assign(this, dto);
+ }
+ }
+
+ @PrimaryGeneratedColumn({ primaryKeyConstraintName: 'PK_OrdemPagamentoAgrupadoHistoricoId' })
+ id: number;
+
+ @Column({ type: Date, unique: false, nullable: false })
+ dataReferencia: Date;
+
+ @Column({ type: String, unique: false, nullable: false })
+ userBankCode: string;
+
+ @Column({ type: String, unique: false, nullable: false })
+ userBankAgency: string;
+
+ @Column({ type: String, unique: false, nullable: false })
+ userBankAccount: string;
+
+ @Column({ type: String, unique: false, nullable: false })
+ userBankAccountDigit: string;
+
+ @Column({ enum: StatusRemessaEnum, unique: false, nullable: false })
+ statusRemessa: StatusRemessaEnum;
+
+ @Column({ enum: OcorrenciaEnum, unique: false, nullable: true })
+ motivoStatusRemessa: OcorrenciaEnum;
+
+ @ManyToOne(() => OrdemPagamentoAgrupado, { eager: false })
+ @JoinColumn({ foreignKeyConstraintName: 'FK_OrdemPagamentoAgrupadoHistorico_ordensPagamento_OneToMany' })
+ ordemPagamentoAgrupado: OrdemPagamentoAgrupado;
+
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/entity/ordem-pagamento-agrupado.entity.ts b/src/cnab/novo-remessa/entity/ordem-pagamento-agrupado.entity.ts
new file mode 100644
index 000000000..562b9605b
--- /dev/null
+++ b/src/cnab/novo-remessa/entity/ordem-pagamento-agrupado.entity.ts
@@ -0,0 +1,52 @@
+import { EntityHelper } from 'src/utils/entity-helper';
+import { Column, CreateDateColumn, DeepPartial, Entity, JoinColumn, ManyToOne, OneToMany,
+ PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
+import { OrdemPagamento } from './ordem-pagamento.entity';
+import { OrdemPagamentoAgrupadoHistorico } from './ordem-pagamento-agrupado-historico.entity';
+import { Pagador } from 'src/cnab/entity/pagamento/pagador.entity';
+
+@Entity()
+export class OrdemPagamentoAgrupado extends EntityHelper {
+ pagadorId: number;
+ constructor(dto?: DeepPartial) {
+ super();
+ if (dto) {
+ Object.assign(this, dto);
+ }
+ }
+
+ @PrimaryGeneratedColumn({ primaryKeyConstraintName: 'PK_OrdemPagamentoAgrupadoId' })
+ id: number;
+
+ @Column({
+ type: 'decimal',
+ unique: false,
+ nullable: false,
+ precision: 13,
+ scale: 5,
+ })
+ valorTotal: number;
+
+ @Column({ type: Date, unique: false, nullable: false })
+ dataPagamento: Date;
+
+ @OneToMany(() => OrdemPagamento, (op) => op.ordemPagamentoAgrupado, { eager: false })
+ @JoinColumn({ foreignKeyConstraintName: 'FK_OrdemPagamentoAgrupado_ordensPagamento_OneToMany' })
+ ordensPagamento: OrdemPagamento[];
+
+ @OneToMany(() => OrdemPagamentoAgrupadoHistorico, (op) => op.ordemPagamentoAgrupado, { eager: true })
+ @JoinColumn({ foreignKeyConstraintName: 'FK_OrdemPagamentoAgrupadoHistorico_ordensPagamento_OneToMany' })
+ ordensPagamentoAgrupadoHistorico: OrdemPagamentoAgrupadoHistorico[];
+
+ @ManyToOne(() => Pagador, { eager: true })
+ @JoinColumn({
+ foreignKeyConstraintName: 'FK_OrdemPagamentoAgrupado_pagador_ManyToOne',
+ })
+ pagador: Pagador;
+
+ @CreateDateColumn()
+ createdAt: Date;
+
+ @UpdateDateColumn()
+ updatedAt: Date;
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/entity/ordem-pagamento.entity.ts b/src/cnab/novo-remessa/entity/ordem-pagamento.entity.ts
new file mode 100644
index 000000000..43bb87f1b
--- /dev/null
+++ b/src/cnab/novo-remessa/entity/ordem-pagamento.entity.ts
@@ -0,0 +1,69 @@
+import { EntityHelper } from 'src/utils/entity-helper';
+import { Column, CreateDateColumn, DeepPartial, Entity, JoinColumn, ManyToOne, PrimaryColumn, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
+import { OrdemPagamentoAgrupado } from './ordem-pagamento-agrupado.entity';
+
+@Entity()
+export class OrdemPagamento extends EntityHelper {
+ constructor(dto?: DeepPartial) {
+ super();
+ if (dto) {
+ Object.assign(this, dto);
+ }
+ }
+
+ /** id_ordem_pagamento_consorcio_operador_dia */
+ @PrimaryColumn({ primaryKeyConstraintName: 'PK_OrdemPagamentoId' })
+ id: number;
+
+ @Column({ type: 'date', unique: false, nullable: false })
+ dataOrdem: Date;
+
+ @Column({ type: String, unique: false, nullable: true, length: 200 })
+ nomeConsorcio: string | null;
+
+ @Column({ type: String, unique: false, nullable: true, length: 200 })
+ nomeOperadora: string | null;
+
+ @Column({ type: Number, unique: false, nullable: true })
+ userId?: number | undefined;
+
+ @Column({
+ type: 'decimal',
+ unique: false,
+ nullable: false,
+ precision: 13,
+ scale: 5,
+ })
+ valor: number;
+
+ @Column({ type: String, unique: false, nullable: true })
+ idOrdemPagamento: string;
+
+ @Column({ type: String, unique: false, nullable: true })
+ idOperadora: string;
+
+ @Column({ type: String, unique: false, nullable: true })
+ operadoraCpfCnpj: string | null;
+
+ @Column({ type: String, unique: false, nullable: true })
+ idConsorcio: string | null;
+
+ @ManyToOne(() => OrdemPagamentoAgrupado, { eager: true })
+ @JoinColumn({ foreignKeyConstraintName: 'FK_OrdemPagamentoAgrupado_ManyToOne' })
+ ordemPagamentoAgrupado: OrdemPagamentoAgrupado;
+
+ @Column({ type: Date, unique: false, nullable: true })
+ dataCaptura: Date | undefined;
+
+ /**
+ *
+ */
+ @Column({ type: Date, unique: false, nullable: false })
+ bqUpdatedAt: Date;
+
+ @CreateDateColumn()
+ createdAt: Date;
+
+ @UpdateDateColumn()
+ updatedAt: Date;
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/repository/distributed-lock.repository.ts b/src/cnab/novo-remessa/repository/distributed-lock.repository.ts
new file mode 100644
index 000000000..e9e0ae948
--- /dev/null
+++ b/src/cnab/novo-remessa/repository/distributed-lock.repository.ts
@@ -0,0 +1,38 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Repository } from 'typeorm';
+import { CustomLogger } from 'src/utils/custom-logger';
+import { OrdemPagamento } from '../entity/ordem-pagamento.entity';
+
+@Injectable()
+export class DistributedLockRepository {
+ private logger = new CustomLogger(DistributedLockRepository.name, { timestamp: true });
+
+ constructor(
+ @InjectRepository(OrdemPagamento)
+ private readonly repository: Repository,
+ ) {}
+
+ public async acquireLock(lockKey: string): Promise {
+ const lockId = this.generateLockId(lockKey);
+ const lockQuery = `SELECT pg_try_advisory_lock(${lockId})`;
+ const result = await this.repository.query(lockQuery);
+ return result[0].pg_try_advisory_lock;
+ }
+
+ public async releaseLock(lockKey: string): Promise {
+ const lockId = this.generateLockId(lockKey);
+ const unlockQuery = `SELECT pg_advisory_unlock(${lockId})`;
+ await this.repository.query(unlockQuery);
+ }
+
+ private generateLockId(lockKey: string): number {
+ let hash = 0;
+ for (let i = 0; i < lockKey.length; i++) {
+ const char = lockKey.charCodeAt(i);
+ hash = (hash << 5) - hash + char;
+ hash |= 0; // Convert to 32bit integer
+ }
+ return hash;
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/repository/ordem-pagamento-agrupado-historico.repository.ts b/src/cnab/novo-remessa/repository/ordem-pagamento-agrupado-historico.repository.ts
new file mode 100644
index 000000000..fba413082
--- /dev/null
+++ b/src/cnab/novo-remessa/repository/ordem-pagamento-agrupado-historico.repository.ts
@@ -0,0 +1,61 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { CustomLogger } from 'src/utils/custom-logger';
+import { EntityCondition } from 'src/utils/types/entity-condition.type';
+import { DataSource, DeepPartial, Repository } from 'typeorm';
+import { OrdemPagamentoAgrupadoHistorico } from '../entity/ordem-pagamento-agrupado-historico.entity';
+import { OrdemPagamentoAgrupadoHistoricoDTO } from '../dto/ordem-pagamento-agrupado-historico.dto';
+
+@Injectable()
+export class OrdemPagamentoAgrupadoHistoricoRepository {
+ private logger = new CustomLogger(OrdemPagamentoAgrupadoHistoricoRepository.name, { timestamp: true });
+
+ constructor(
+ @InjectRepository(OrdemPagamentoAgrupadoHistorico)
+ private ordemPagamentoAgrupadoHistoricoRepository: Repository,
+ private readonly dataSource: DataSource
+ ) {}
+
+ public async save(dto: DeepPartial): Promise {
+ return this.ordemPagamentoAgrupadoHistoricoRepository.save(dto);
+ }
+
+ public async findOne(fields: EntityCondition): Promise {
+ return await this.ordemPagamentoAgrupadoHistoricoRepository.find({
+ where: fields,
+ order: {
+ id: 'DESC',
+ },
+ });
+ }
+
+ public async findAll(fields: EntityCondition): Promise {
+ return await this.ordemPagamentoAgrupadoHistoricoRepository.find({
+ where: fields,
+ });
+ }
+
+ public async getHistoricoDetalheA(detalheAId: number):Promise{
+
+ const query = (`select distinct u."fullName" userName, u."cpfCnpj" usercpfcnpj,
+ oph.* from ordem_pagamento_agrupado_historico oph
+ inner join detalhe_a da on da."ordemPagamentoAgrupadoHistoricoId"= oph.id
+ left join ordem_pagamento_agrupado opa on opa."id" = oph."ordemPagamentoAgrupadoId"
+ left join ordem_pagamento op on op."ordemPagamentoAgrupadoId" = opa.id
+ left join public.user u on u."id" = op."userId"` +
+ `where da."id" = ${detalheAId}`)
+
+ const queryRunner = this.dataSource.createQueryRunner();
+
+ queryRunner.connect();
+
+ const result: any[] = await queryRunner.manager.query(query);
+
+ const oph = result.map((i) => new OrdemPagamentoAgrupadoHistoricoDTO(i));
+
+ queryRunner.release()
+
+ return oph[0];
+ }
+
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/repository/ordem-pagamento-agrupado.repository.ts b/src/cnab/novo-remessa/repository/ordem-pagamento-agrupado.repository.ts
new file mode 100644
index 000000000..2d5c3510d
--- /dev/null
+++ b/src/cnab/novo-remessa/repository/ordem-pagamento-agrupado.repository.ts
@@ -0,0 +1,74 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { CustomLogger } from 'src/utils/custom-logger';
+import { EntityCondition } from 'src/utils/types/entity-condition.type';
+import { Nullable } from 'src/utils/types/nullable.type';
+import { DataSource, DeepPartial, Repository } from 'typeorm';
+import { OrdemPagamentoAgrupado } from '../entity/ordem-pagamento-agrupado.entity';
+import { StatusRemessaEnum } from 'src/cnab/enums/novo-remessa/status-remessa.enum';
+import { formatDateISODate } from 'src/utils/date-utils';
+
+@Injectable()
+export class OrdemPagamentoAgrupadoRepository {
+ private logger = new CustomLogger(OrdemPagamentoAgrupadoRepository.name, { timestamp: true });
+
+ constructor(
+ @InjectRepository(OrdemPagamentoAgrupado)
+ private ordemPagamentoAgrupadoRepository: Repository,
+ private readonly dataSource: DataSource,
+ ) {}
+
+ public async save(dto: DeepPartial): Promise {
+ return this.ordemPagamentoAgrupadoRepository.save(dto);
+ }
+
+ public async saveAll(dtos: DeepPartial[]): Promise {
+ return this.ordemPagamentoAgrupadoRepository.save(dtos);
+ }
+
+ public async findOne(fields: EntityCondition): Promise> {
+ return await this.ordemPagamentoAgrupadoRepository.findOne({
+ where: fields,
+ });
+ }
+
+ public async findAll(): Promise {
+ return await this.ordemPagamentoAgrupadoRepository.find({});
+ }
+
+ public async findAllCustom(dataInicio:Date,dataFim:Date,nomeConsorcio?:string[],statusRemessa?:StatusRemessaEnum): Promise {
+ const dataIniForm = formatDateISODate(dataInicio)
+ const dataFimForm = formatDateISODate(dataFim)
+ let query = ` select distinct opa.* from ordem_pagamento op
+ inner join ordem_pagamento_agrupado opa on opa.id = op."ordemPagamentoAgrupadoId"
+ inner join ordem_pagamento_agrupado_historico oph on opa.id = oph."ordemPagamentoAgrupadoId"
+ and oph."dataReferencia" =
+ (select max(ophh."dataReferencia") from ordem_pagamento_agrupado_historico ophh
+ where ophh."ordemPagamentoAgrupadoId"=op."ordemPagamentoAgrupadoId")
+ where (1=1) `
+
+ if(statusRemessa === undefined || statusRemessa === StatusRemessaEnum.Criado){
+ query = query +` and oph."statusRemessa"= 0 `;
+ }else{
+ query = query +` and oph."statusRemessa"=${statusRemessa} `;
+ }
+
+ if(dataInicio!==undefined && dataFim!==undefined && dataFim >=dataInicio){
+ query = query +` and op."dataCaptura" between '${dataIniForm} 00:00:00' and '${dataFimForm} 23:59:59'
+ and op."ordemPagamentoAgrupadoId" is not null `;
+ }else{
+ return [];
+ }
+
+ if(nomeConsorcio) {
+ query = query +` and op."nomeConsorcio" in ('${nomeConsorcio.join("','")}') `;
+ }
+
+ this.logger.debug(query);
+ const queryRunner = this.dataSource.createQueryRunner();
+ await queryRunner.connect();
+ let result: any[] = await queryRunner.query(query);
+ queryRunner.release();
+ return result.map((r) => new OrdemPagamentoAgrupado(r));
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/repository/ordem-pagamento.repository.ts b/src/cnab/novo-remessa/repository/ordem-pagamento.repository.ts
new file mode 100644
index 000000000..298cb4911
--- /dev/null
+++ b/src/cnab/novo-remessa/repository/ordem-pagamento.repository.ts
@@ -0,0 +1,358 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { CustomLogger } from 'src/utils/custom-logger';
+import { EntityCondition } from 'src/utils/types/entity-condition.type';
+import { Nullable } from 'src/utils/types/nullable.type';
+import { DataSource, DeepPartial, Repository } from 'typeorm';
+import { OrdemPagamento } from '../entity/ordem-pagamento.entity';
+import { OrdemPagamentoAgrupadoMensalDto } from '../dto/ordem-pagamento-agrupado-mensal.dto';
+import { OrdemPagamentoSemanalDto } from '../dto/ordem-pagamento-semanal.dto';
+import { getStatusRemessaEnumByValue } from '../../enums/novo-remessa/status-remessa.enum';
+import { OcorrenciaEnum } from '../../enums/ocorrencia.enum';
+import { OrdemPagamentoPendenteDto } from '../dto/ordem-pagamento-pendente.dto';
+import { OrdemPagamentoPendenteNuncaRemetidasDto } from '../dto/ordem-pagamento-pendente-nunca-remetidas.dto';
+import { Pagador } from '../../entity/pagamento/pagador.entity';
+import { parseNumber } from '../../utils/cnab/cnab-field-utils';
+
+@Injectable()
+export class OrdemPagamentoRepository {
+ private logger = new CustomLogger(OrdemPagamentoRepository.name, { timestamp: true });
+
+ constructor(
+ @InjectRepository(OrdemPagamento)
+ private ordemPagamentoRepository: Repository,
+ private readonly dataSource: DataSource
+ ) { }
+
+ public async save(dto: DeepPartial): Promise {
+ const existing = await this.ordemPagamentoRepository.findOneBy({ id: dto.id });
+ if (existing) {
+ return existing;
+ }
+ const createdOrdem = this.ordemPagamentoRepository.create(dto);
+ return this.ordemPagamentoRepository.save(createdOrdem);
+ }
+
+ public async findOne(fields: EntityCondition): Promise> {
+ return await this.ordemPagamentoRepository.findOne({
+ where: fields,
+ });
+ }
+
+ public async findAll(fields: EntityCondition): Promise {
+ return await this.ordemPagamentoRepository.find({
+ where: fields,
+ });
+ }
+
+ public async findOrdensPagamentoAgrupadasPorMes(userId: number, targetDate: Date): Promise {
+ const query = `
+ WITH month_dates AS (SELECT generate_series(
+ DATE_TRUNC('month', $1::DATE),
+ DATE_TRUNC('month', $1::DATE) + INTERVAL '1 month' - INTERVAL '1 day',
+ '1 day'
+ ) AS data)
+ SELECT data,
+ (SELECT ROUND("valorTotal", 2)
+ FROM ordem_pagamento_agrupado opa
+ WHERE 1 = 1
+ AND DATE_TRUNC('day', opa."dataPagamento") = data::date
+ AND EXISTS (SELECT 1
+ FROM ordem_pagamento op
+ WHERE op."userId" = $2
+ AND op."dataCaptura" IS NOT NULL
+ AND op."ordemPagamentoAgrupadoId" = opa.id
+ LIMIT 1)
+ ORDER BY opa."dataPagamento" DESC
+ LIMIT 1 ) "valorTotal",
+ (data::date - 1) data_final_operacoes,
+ (data::date - 7) data_inicial_operacoes,
+ "dataReferencia",
+ "statusRemessa",
+ "motivoStatusRemessa",
+ "ordemPagamentoAgrupadoId"
+ FROM month_dates m
+ LEFT JOIN LATERAL (
+ SELECT opah."dataReferencia", opah."statusRemessa", opah."motivoStatusRemessa", opah."ordemPagamentoAgrupadoId", opa."dataPagamento"
+ FROM ordem_pagamento_agrupado_historico opah
+ INNER JOIN ordem_pagamento_agrupado opa
+ ON opa.id = opah."ordemPagamentoAgrupadoId"
+ INNER JOIN ordem_pagamento op
+ ON op."ordemPagamentoAgrupadoId" = opa.id
+ WHERE 1 = 1
+ AND op."userId" = $2
+ AND DATE_TRUNC('day', opa."dataPagamento") = DATE_TRUNC('day', m.data)
+ ORDER BY opah."dataReferencia" DESC
+ LIMIT 1
+ ) opa_aux
+ ON DATE_TRUNC('day', opa_aux."dataPagamento") = DATE_TRUNC('day', m.data)
+ WHERE EXTRACT (DOW FROM data) = 5
+ `;
+
+ const result = await this.ordemPagamentoRepository.query(query, [targetDate, userId]);
+ return result.map((row: any) => {
+ const dto = new OrdemPagamentoAgrupadoMensalDto();
+ dto.data = row.data;
+ dto.ordemPagamentoAgrupadoId = row.ordemPagamentoAgrupadoId;
+ dto.valorTotal = row.valorTotal != null ? parseFloat(row.valorTotal) : 0;
+ if (row.motivoStatusRemessa != null) {
+ dto.motivoStatusRemessa = row.motivoStatusRemessa;
+ dto.descricaoMotivoStatusRemessa = OcorrenciaEnum[row.motivoStatusRemessa];
+ }
+ if (row.statusRemessa != null) {
+ dto.statusRemessa = row.statusRemessa;
+ dto.descricaoStatusRemessa = getStatusRemessaEnumByValue(row.statusRemessa);
+ }
+ return dto;
+ });
+ }
+
+ /***
+ * Obtém as ordens que foram agrupadas mas o pagamento falhou.
+ * Existem dois tipos de falhas:
+ * - Falhas nas quais o usuário deve modificar os dados bancários.
+ * - Falhas nas quais houve um erro desconhecido no arquivo de retorno do banco
+ * Não são retornadas ordens que o usuário não modificou os dados bancários caso houveram os erros referentes a conta
+ * do usuário.
+ * @returns OrdemPagamentoPendenteDto[] - Lista de ordens de pagamento pendentes
+ * */
+ public async findOrdensPagamentosPendentes(): Promise {
+ const query = `select o.id,
+ o."valor",
+ "dataPagamento",
+ "dataReferencia",
+ "statusRemessa",
+ "motivoStatusRemessa",
+ opa.id as "ordemPagamentoAgrupadoId",
+ "userId"
+ from ordem_pagamento o
+ inner join ordem_pagamento_agrupado opa
+ on o."ordemPagamentoAgrupadoId" = opa.id
+ inner join lateral (
+ select "dataReferencia",
+ "statusRemessa",
+ "motivoStatusRemessa",
+ "ordemPagamentoAgrupadoId",
+ "userBankCode",
+ "userBankAgency",
+ "userBankAccount",
+ "userBankAccountDigit"
+ from ordem_pagamento_agrupado_historico oph
+ where opa.id = oph."ordemPagamentoAgrupadoId"
+ order by oph."dataReferencia" desc
+ limit 1
+ ) oph
+ on opa.id = oph."ordemPagamentoAgrupadoId"
+ inner join "user" u
+ on o."userId" = u.id
+ where "statusRemessa" = 4
+ and "motivoStatusRemessa" NOT IN ('00', 'BD')
+ and o."userId" is not null
+ and u."bankAccount" is not null
+ and u."bankAgency" is not null
+ and u."bankCode" is not null
+ and u."bankAccountDigit" is not null
+ and (
+ (
+ "motivoStatusRemessa" NOT IN ('AG', 'AM', 'AN', 'AZ', 'BA', '02')
+ )
+ or (
+ "motivoStatusRemessa" IN ('AG', 'AM', 'AN', 'AZ', 'BA', '02')
+ and u."bankAccount" <> oph."userBankAccount"
+ and u."bankAgency" <> oph."userBankAgency"
+ and u."bankCode" <> cast(oph."userBankCode" as integer)
+ and u."bankAccountDigit" <> oph."userBankAccountDigit"
+ )
+ )`;
+ const result = await this.ordemPagamentoRepository.query(query);
+ return result.map((row: any) => {
+ const ordemPagamentoPendente = new OrdemPagamentoPendenteDto();
+ ordemPagamentoPendente.id = row.id;
+ ordemPagamentoPendente.valor = row.valoe ? parseFloat(row.valor) : 0;
+ ordemPagamentoPendente.dataPagamento = row.dataPagamento;
+ ordemPagamentoPendente.dataReferencia = row.dataReferencia;
+ ordemPagamentoPendente.statusRemessa = row.statusRemessa;
+ ordemPagamentoPendente.motivoStatusRemessa = row.motivoStatusRemessa;
+ ordemPagamentoPendente.ordemPagamentoAgrupadoId = row.ordemPagamentoAgrupadoId;
+ ordemPagamentoPendente.userId = row.userId;
+ });
+ }
+
+ /***
+ Busca as ordens que não foram agrupadas e pagas
+ São ignoradas as seguintes situações
+ situações:
+ - Usuário não cadastrou dados bancários
+ - Usuário não se cadastrou no CCT, e foi gerada uma ordem com ID null
+ @param userId - Id do usuário (opcional)
+ @returns OrdemPagamentoPendenteNuncaRemetidasDto[] - Lista de ordens de pagamento pendentes
+ */
+ public async findOrdensPagamentosPendentesQueNuncaForamRemetidas(userId?: number | undefined): Promise {
+ let query = `
+ select o.id,
+ o.valor,
+ o."dataOrdem",
+ "userId",
+ "ultimaDataPagamento"
+ from ordem_pagamento o
+ left join ordem_pagamento_agrupado opa
+ on o."ordemPagamentoAgrupadoId" = opa.id
+ left join "user" u
+ on o."userId" = u.id
+ inner join lateral (
+ select date_trunc('day', max(opa2."dataPagamento")) as "ultimaDataPagamento"
+ from ordem_pagamento_agrupado opa2
+ where date_trunc('day', opa2."dataPagamento") <= date_trunc('day', current_date)
+ ) ultimo_pagamento
+ on true
+ where 1 = 1
+ and opa.id is null
+ and (
+ o."userId" is not null
+ and u."bankAccount" is not null
+ and u."bankAgency" is not null
+ and u."bankCode" is not null
+ and u."bankAccountDigit" is not null
+ )
+ and date_trunc('day', "dataOrdem") <= ultimo_pagamento."ultimaDataPagamento"`;
+ if (userId) {
+ query += ` and o."userId" = $1`;
+ const result = await this.ordemPagamentoRepository.query(query, [userId]);
+ return result.map((row: any) => {
+ const ordemPagamentoPendente = new OrdemPagamentoPendenteNuncaRemetidasDto();
+ ordemPagamentoPendente.id = row.id;
+ ordemPagamentoPendente.valor = row.valor ? parseFloat(Number(row.valor).toFixed(2)) : 0;
+ ordemPagamentoPendente.userId = row.userId;
+ ordemPagamentoPendente.dataOrdem = row.dataOrdem;
+ return ordemPagamentoPendente;
+ });
+ } else {
+ const result = await this.ordemPagamentoRepository.query(query);
+ return result.map((row: any) => {
+ const ordemPagamentoPendente = new OrdemPagamentoPendenteNuncaRemetidasDto();
+ ordemPagamentoPendente.id = row.id;
+ ordemPagamentoPendente.valor = row.valor ? parseFloat(Number(row.valor).toFixed(2)) : 0;
+ ordemPagamentoPendente.userId = row.userId;
+ ordemPagamentoPendente.dataOrdem = row.dataOrdem;
+ return ordemPagamentoPendente;
+ });
+ }
+ }
+
+ public async findOrdensPagamentoByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId: number, userId: number): Promise {
+ const query = `
+ SELECT o.id,
+ ROUND(valor, 2) valor,
+ o."dataOrdem"
+ FROM ordem_pagamento o
+ INNER JOIN ordem_pagamento_agrupado opa
+ ON o."ordemPagamentoAgrupadoId" = opa.id
+ WHERE 1 = 1
+ AND opa.id = $1
+ AND o."dataCaptura" IS NOT NULL
+ AND o."userId" = $2
+ AND date_trunc('day', o."dataOrdem") BETWEEN date_trunc('day', "dataPagamento") - INTERVAL '7 days' AND date_trunc('day', "dataPagamento") - INTERVAL '1 day'
+ ORDER BY o."dataOrdem" desc
+ `;
+
+ const result = await this.ordemPagamentoRepository.query(query, [ordemPagamentoAgrupadoId, userId]);
+ return result.map((row: any) => {
+ const ordemPagamento = new OrdemPagamentoSemanalDto();
+ ordemPagamento.ordemId = row.id;
+ ordemPagamento.dataOrdem = row.dataOrdem;
+ ordemPagamento.valor = row.valor ? parseFloat(row.valor) : 0;
+ return ordemPagamento;
+ });
+ }
+
+ public async findOrdensPagamentoAgrupadasByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId: number, userId: number): Promise {
+ const query = `
+ SELECT o.id,
+ ROUND(valor, 2) valor,
+ date_trunc('day', o."dataCaptura") "dataCaptura"
+ FROM ordem_pagamento o
+ INNER JOIN ordem_pagamento_agrupado opa
+ ON o."ordemPagamentoAgrupadoId" = opa.id
+ WHERE 1 = 1
+ AND opa.id = $1
+ AND o."dataCaptura" IS NOT NULL
+ AND o."userId" = $2
+ ORDER BY o."dataCaptura" desc
+ `;
+
+ let result = await this.ordemPagamentoRepository.query(query, [ordemPagamentoAgrupadoId, userId]);
+ result = result.map((row: any) => {
+ const ordemPagamento = new OrdemPagamentoSemanalDto();
+ ordemPagamento.ordemId = row.id;
+ ordemPagamento.dataCaptura = row.dataCaptura;
+ ordemPagamento.valor = row.valor ? parseFloat(row.valor) : 0;
+ return ordemPagamento;
+ });
+
+ const resultGrouped: OrdemPagamentoSemanalDto[] = [];
+
+ for (const row of result) {
+ const existing = resultGrouped.find((item) => item.dataCaptura?.toISOString() == row.dataCaptura.toISOString());
+ if (existing) {
+ existing.valor += row.valor;
+ if (!existing.ids) {
+ existing.ids = [];
+ }
+ existing.ids.push(row.ordemId);
+ existing.ordemId = undefined;
+ } else {
+ row.ids = [row.ordemId];
+ row.ordemId = undefined;
+ resultGrouped.push(row);
+ }
+ }
+ return resultGrouped;
+ }
+
+ public async findOrdensPagamentoDiasAnterioresByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId: number, userId: number): Promise {
+ const query = `
+ SELECT SUM(ROUND(valor, 2)) valor,
+ o."dataOrdem",
+ o."dataCaptura"
+ FROM ordem_pagamento o
+ INNER JOIN ordem_pagamento_agrupado opa
+ ON o."ordemPagamentoAgrupadoId" = opa.id
+ WHERE 1 = 1
+ AND opa.id = $1
+ AND o."dataCaptura" IS NOT NULL
+ AND o."userId" = $2
+ AND date_trunc('day', o."dataOrdem") < date_trunc('day', "dataPagamento") - INTERVAL '7 days'
+ GROUP BY o."dataOrdem", o."dataCaptura"
+ ORDER BY o."dataOrdem" desc
+ `;
+
+ const result = await this.ordemPagamentoRepository.query(query, [ordemPagamentoAgrupadoId, userId]);
+ return result.map((row: any) => {
+ const ordemPagamento = new OrdemPagamentoSemanalDto();
+ ordemPagamento.dataOrdem = row.dataOrdem;
+ ordemPagamento.valor = row.valor ? parseFloat(row.valor) : 0;
+ ordemPagamento.dataCaptura = row.dataCaptura;
+ return ordemPagamento;
+ });
+ }
+
+ public async agruparOrdensDePagamento(dataInicial: Date, dataFinal: Date, dataPgto: Date, pagador: Pagador, consorcios: string[]): Promise {
+ const dtInicialStr = dataInicial.toISOString().split('T')[0];
+ const dtFinalStr = dataFinal.toISOString().split('T')[0];
+ const dtPgtoStr = dataPgto.toISOString().split('T')[0];
+ const consorciosJoin = consorcios.join(',');
+ await this.ordemPagamentoRepository.query(`CALL P_AGRUPAR_ORDENS($1, $2, $3, $4, $5)`, [`${dtInicialStr} 00:00:00`, `${dtFinalStr} 23:59:59`, dtPgtoStr, pagador.id, `{${consorciosJoin}}`]);
+ }
+
+ async findNumeroOrdensPorIntervaloDataCaptura(startDate: Date, endDate: Date) {
+ // Query max dataCaptura
+ const query = `SELECT COUNT(*) as qtde FROM ordem_pagamento op
+ where date_trunc('day', "dataCaptura") between $1 and $2`;
+ const result = await this.ordemPagamentoRepository.query(query, [startDate, endDate]);
+ if (result.length > 0) {
+ return parseFloat(result[0].qtde);
+ }
+ return Promise.resolve(undefined);
+ }
+
+}
diff --git a/src/cnab/novo-remessa/service/cron-remessa.module.txt b/src/cnab/novo-remessa/service/cron-remessa.module.txt
new file mode 100644
index 000000000..07983d249
--- /dev/null
+++ b/src/cnab/novo-remessa/service/cron-remessa.module.txt
@@ -0,0 +1,15 @@
+import { Module } from '@nestjs/common';
+import { ConfigModule } from '@nestjs/config';
+import { ScheduleModule } from '@nestjs/schedule';
+import { CronRemessaService } from './cron-remessa.service';
+import { OrdemPagamentoAgrupadoService } from './ordem-pagamento-agrupado.service';
+
+
+@Module({
+ imports: [
+ ScheduleModule.forRoot(),
+ ],
+ providers: [CronRemessaService],
+ exports: [CronRemessaService],
+})
+export class CronRemessaModule {}
diff --git a/src/cnab/novo-remessa/service/cron-remessa.service.txt b/src/cnab/novo-remessa/service/cron-remessa.service.txt
new file mode 100644
index 000000000..892323166
--- /dev/null
+++ b/src/cnab/novo-remessa/service/cron-remessa.service.txt
@@ -0,0 +1,105 @@
+import { Injectable } from "@nestjs/common";
+import { SchedulerRegistry } from "@nestjs/schedule/dist/scheduler.registry";
+import { CronJob, CronJobParameters } from "cron";
+import { CustomLogger } from "src/utils/custom-logger";
+import { OrdemPagamentoAgrupadoService } from "./ordem-pagamento-agrupado.service";
+import { RemessaService } from "./remessa.service";
+
+
+interface ICronJob {
+ name: string;
+ cronJobParameters: CronJobParameters;
+}
+
+/**
+ * CronJob tasks and management
+ */
+@Injectable()
+export class CronRemessaService {
+ private logger = new CustomLogger(CronRemessaService.name, { timestamp: true });
+
+ public jobsConfig: ICronJob[] = [];
+
+ constructor(
+ private schedulerRegistry: SchedulerRegistry,
+ private ordemPagamentoAgrupadoService: OrdemPagamentoAgrupadoService,
+ private remessaService: RemessaService
+ ) { }
+
+ onModuleInit() {
+ this.onModuleLoad().catch((error: Error) => {
+ throw error;
+ });
+ }
+
+ async onModuleLoad() {
+
+ // //Todo: Definir dia/hora
+ // this.jobsConfig.push({
+ // name: "AgrupamentoOrdensPagamento",cronJobParameters: {cronTime: '0 6 * * *',onTick: async () => this.agrupamentoOrdensPagamento()},
+ // });
+
+ // this.jobsConfig.push({
+ // name: "VLT",cronJobParameters: {cronTime: '0 7 * * *',onTick: async () => this.geracaoRemessaVLT()},
+ // });
+
+ // //Todo: Definir dia/hora
+ // this.jobsConfig.push({
+ // name: "Consorcios",cronJobParameters: {cronTime: '0 7 * * *',onTick: async () => this.geracaoRemessaConsorcios()},
+ // });
+
+ // //Todo: Definir dia/hora
+ // this.jobsConfig.push({
+ // name: "Vans",cronJobParameters: {cronTime: '0 7 * * *',onTick: async () => this.geracaoRemessaVans()},
+ // });
+
+ this.executeJobs();
+ }
+
+ //Agru
+ //TODO: Definir dia e Hora para rodar
+
+
+ private async geracaoRemessaVLT(){
+ const dataInicial = new Date()
+ const dataFinal = new Date()
+ this.remessaService.prepararRemessa(dataInicial, dataFinal,["VLT"]) //preparação remessa
+ const list = await this.remessaService.gerarCnabText(new Date()) //Adicionar data pagamento - geração txt
+ this.remessaService.enviarRemessa(list) //envio
+ }
+
+ private async geracaoRemessaConsorcios(){
+
+ }
+
+ private async geracaoRemessaVans(){
+ }
+
+
+ executeJobs(){
+ for (const jobConfig of this.jobsConfig) {
+ this.startCron(jobConfig);
+ this.logger.log(`Tarefa agendada: ${jobConfig.name}, ${jobConfig.cronJobParameters.cronTime}`);
+ }
+ }
+
+ private startCron(jobConfig: ICronJob) {
+ const job = new CronJob(jobConfig.cronJobParameters);
+ this.schedulerRegistry.addCronJob(jobConfig.name, job);
+ job.start();
+ }
+
+
+ configVLT(){
+
+ }
+
+ configConsorcios(){
+
+ }
+
+ configVan(){
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/service/distributed-lock.service.ts b/src/cnab/novo-remessa/service/distributed-lock.service.ts
new file mode 100644
index 000000000..d078d79ca
--- /dev/null
+++ b/src/cnab/novo-remessa/service/distributed-lock.service.ts
@@ -0,0 +1,20 @@
+import { Injectable } from '@nestjs/common';
+import { DistributedLockRepository } from '../repository/distributed-lock.repository';
+import { CustomLogger } from 'src/utils/custom-logger';
+
+@Injectable()
+export class DistributedLockService {
+ private logger = new CustomLogger(DistributedLockService.name, { timestamp: true });
+
+ constructor(private readonly distributedLockRepository: DistributedLockRepository) {}
+
+ public async acquireLock(lockKey: string): Promise {
+ this.logger.debug(`Attempting to acquire lock for key: ${lockKey}`);
+ return await this.distributedLockRepository.acquireLock(lockKey);
+ }
+
+ public async releaseLock(lockKey: string): Promise {
+ this.logger.debug(`Releasing lock for key: ${lockKey}`);
+ await this.distributedLockRepository.releaseLock(lockKey);
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/service/ordem-pagamento-agrupado.service.ts b/src/cnab/novo-remessa/service/ordem-pagamento-agrupado.service.ts
new file mode 100644
index 000000000..35c170a88
--- /dev/null
+++ b/src/cnab/novo-remessa/service/ordem-pagamento-agrupado.service.ts
@@ -0,0 +1,65 @@
+import { Injectable } from '@nestjs/common';
+import { AllPagadorDict } from 'src/cnab/interfaces/pagamento/all-pagador-dict.interface';
+import { PagadorService } from 'src/cnab/service/pagamento/pagador.service';
+import { CustomLogger } from 'src/utils/custom-logger';
+import { OrdemPagamentoAgrupadoRepository } from '../repository/ordem-pagamento-agrupado.repository';
+import { OrdemPagamentoRepository } from '../repository/ordem-pagamento.repository';
+import { Pagador } from 'src/cnab/entity/pagamento/pagador.entity';
+import { OrdemPagamentoAgrupadoHistoricoRepository } from '../repository/ordem-pagamento-agrupado-historico.repository';
+import { OrdemPagamentoAgrupadoHistorico } from '../entity/ordem-pagamento-agrupado-historico.entity';
+import { StatusRemessaEnum } from 'src/cnab/enums/novo-remessa/status-remessa.enum';
+
+@Injectable()
+export class OrdemPagamentoAgrupadoService {
+
+ private logger = new CustomLogger(OrdemPagamentoAgrupadoService.name, { timestamp: true });
+
+ constructor(
+ private ordemPagamentoRepository: OrdemPagamentoRepository,
+ private ordemPagamentoAgrupadoRepository: OrdemPagamentoAgrupadoRepository,
+ private ordemPagamentoAgrupadoHistRepository: OrdemPagamentoAgrupadoHistoricoRepository,
+ private pagadorService: PagadorService,
+ ) {}
+
+ async prepararPagamentoAgrupados(dataOrdemInicial: Date, dataOrdemFinal: Date, dataPgto:Date,
+ pagadorKey: keyof AllPagadorDict,consorcios:string[]) {
+ this.logger.debug(`Preparando agrupamentos`)
+ const pagador = await this.getPagador(pagadorKey);
+ if(pagador) {
+ this.logger.log(`Agrupando ordens de pagamento para o pagador ${pagador}, data de pagamento ${dataPgto}, data de ordem inicial ${dataOrdemInicial}, data de ordem final ${dataOrdemFinal}, consorcios ${consorcios}`);
+ await this.agruparOrdens(dataOrdemInicial, dataOrdemFinal, dataPgto, pagador, consorcios);
+ this.logger.log(`Ordens agrupadas para o pagador ${pagador}, data de pagamento ${dataPgto}, data de ordem inicial ${dataOrdemInicial}, data de ordem final ${dataOrdemFinal}`);
+ }
+ }
+
+ private async agruparOrdens(dataInicial: Date, dataFinal: Date, dataPgto:Date, pagador: Pagador,consorcios: string[]) {
+ await this.ordemPagamentoRepository.agruparOrdensDePagamento(dataInicial, dataFinal, dataPgto, pagador,consorcios);
+ }
+
+ async getOrdens(dataInicio: Date, dataFim: Date, consorcio: string[] | undefined) {
+ return await this.ordemPagamentoAgrupadoRepository.findAllCustom(dataInicio,dataFim,consorcio);
+ }
+
+
+ async getHistoricosOrdem(idOrdem: number){
+ return await this.ordemPagamentoAgrupadoHistRepository.findAll({ ordemPagamentoAgrupado: { id: idOrdem } });
+ }
+
+
+ async saveStatusHistorico(historico: OrdemPagamentoAgrupadoHistorico,statusRemessa:StatusRemessaEnum){
+ historico.statusRemessa = statusRemessa;
+ this.ordemPagamentoAgrupadoHistRepository.save(historico);
+ }
+
+ private async getPagador(pagadorKey: any) {
+ return (await this.pagadorService.getAllPagador())[pagadorKey];
+ }
+
+ public async getOrdemPagamento(idOrdemPagamentoAg: number){
+ return await this.ordemPagamentoRepository.findOne({ordemPagamentoAgrupado:{ id: idOrdemPagamentoAg} })
+ }
+
+ public async getHistoricosOrdemDetalheA(id: number) {
+ return await this.ordemPagamentoAgrupadoHistRepository.getHistoricoDetalheA(id)
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/service/ordem-pagamento.service.spec.ts b/src/cnab/novo-remessa/service/ordem-pagamento.service.spec.ts
new file mode 100644
index 000000000..cfe462ca9
--- /dev/null
+++ b/src/cnab/novo-remessa/service/ordem-pagamento.service.spec.ts
@@ -0,0 +1,136 @@
+import { OrdemPagamentoService } from './ordem-pagamento.service';
+import { OrdemPagamentoRepository } from '../repository/ordem-pagamento.repository';
+import { Test, TestingModule } from '@nestjs/testing';
+import { BigqueryOrdemPagamentoService } from '../../../bigquery/services/bigquery-ordem-pagamento.service';
+import { UsersService } from '../../../users/users.service';
+import { CustomLogger } from '../../../utils/custom-logger';
+import { nextFriday } from 'date-fns';
+import { getStatusRemessaEnumByValue, StatusRemessaEnum } from '../../enums/novo-remessa/status-remessa.enum';
+import { getDescricaoOcorrenciaEnumByValue, OcorrenciaEnum } from '../../enums/ocorrencia.enum';
+import { OrdemPagamentoSemanalDto } from '../dto/ordem-pagamento-semanal.dto';
+import { OrdemPagamentoMensalDto } from '../dto/ordem-pagamento-mensal.dto';
+
+describe('OrdemPagamentoService', () => {
+ let service: OrdemPagamentoService;
+ let ordemPagamentoRepository: OrdemPagamentoRepository;
+
+ beforeEach(async () => {
+ const module: TestingModule = await Test.createTestingModule({
+ providers: [
+ OrdemPagamentoService,
+ {
+ provide: OrdemPagamentoRepository,
+ useValue: {
+ findOrdensPagamentoAgrupadasPorMes: jest.fn(),
+ findOrdensPagamentoByOrdemPagamentoAgrupadoId: jest.fn(),
+ },
+ },
+ {
+ provide: BigqueryOrdemPagamentoService,
+ useValue: {},
+ },
+ {
+ provide: UsersService,
+ useValue: {},
+ },
+ {
+ provide: CustomLogger,
+ useValue: {
+ debug: jest.fn(),
+ error: jest.fn(),
+ },
+ },
+ ],
+ }).compile();
+
+ service = module.get(OrdemPagamentoService);
+ ordemPagamentoRepository = module.get(OrdemPagamentoRepository);
+ });
+
+ describe('findOrdensPagamentoAgrupadasPorMes', () => {
+ it('should return an instance of OrdemPagamentoMensalDto', async () => {
+ const userId = 1;
+ const yearMonth = new Date();
+ const ordensDoMes = [
+ {
+ ordemPagamentoAgrupadoId: 100,
+ valorTotal: 100,
+ data: nextFriday(new Date()),
+ statusRemessa: 2,
+ motivoStatusRemessa: 'AA',
+ descricaoMotivoStatusRemessa: getDescricaoOcorrenciaEnumByValue(OcorrenciaEnum['AA']),
+ descricaoStatusRemessa: getStatusRemessaEnumByValue(StatusRemessaEnum.NaoEfetivado),
+ },
+ ];
+
+ const expectedResult: OrdemPagamentoMensalDto = {
+ ordens: ordensDoMes,
+ valorTotal: 100,
+ valorTotalPago: 0,
+ };
+
+ jest.spyOn(ordemPagamentoRepository, 'findOrdensPagamentoAgrupadasPorMes').mockResolvedValue(ordensDoMes);
+
+ const result = await service.findOrdensPagamentoAgrupadasPorMes(userId, yearMonth);
+
+ expect(result).toEqual(expectedResult);
+ expect(ordemPagamentoRepository.findOrdensPagamentoAgrupadasPorMes).toHaveBeenCalledWith(userId, yearMonth);
+ });
+ });
+
+
+ describe('findOrdensPagamentoAgrupadasPorMesComValorPago', () => {
+ it('should return an instance of OrdemPagamentoMensalDto', async () => {
+ const userId = 1;
+ const yearMonth = new Date();
+ const ordensDoMes = [
+ {
+ ordemPagamentoAgrupadoId: 100,
+ valorTotal: 100,
+ data: nextFriday(new Date()),
+ statusRemessa: 3,
+ motivoStatusRemessa: '00',
+ descricaoMotivoStatusRemessa: getDescricaoOcorrenciaEnumByValue(OcorrenciaEnum['00']),
+ descricaoStatusRemessa: getStatusRemessaEnumByValue(StatusRemessaEnum.Efetivado),
+ },
+ ];
+
+ const expectedResult: OrdemPagamentoMensalDto = {
+ ordens: ordensDoMes,
+ valorTotal: 100,
+ valorTotalPago: 100,
+ };
+
+ jest.spyOn(ordemPagamentoRepository, 'findOrdensPagamentoAgrupadasPorMes').mockResolvedValue(ordensDoMes);
+
+ const result = await service.findOrdensPagamentoAgrupadasPorMes(userId, yearMonth);
+
+ expect(result).toEqual(expectedResult);
+ expect(ordemPagamentoRepository.findOrdensPagamentoAgrupadasPorMes).toHaveBeenCalledWith(userId, yearMonth);
+ });
+ });
+
+
+ describe('findOrdensPagamentoByOrdemPagamentoAgrupadoId', () => {
+ it('should return an array of OrdemPagamentoDiarioDto', async () => {
+ const ordemPagamentoAgrupadoId = 1;
+ const expectedResult: OrdemPagamentoSemanalDto[] = [
+ {
+ valor: 100,
+ dataOrdem: new Date(),
+ dataReferencia: new Date(),
+ statusRemessa: 2,
+ motivoStatusRemessa: 'AA',
+ ordemId: 1,
+ },
+ ];
+
+ jest.spyOn(ordemPagamentoRepository, 'findOrdensPagamentoByOrdemPagamentoAgrupadoId').mockResolvedValue(expectedResult);
+
+ const result = await service.findOrdensPagamentoByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId);
+
+ expect(result).toEqual(expectedResult);
+ expect(ordemPagamentoRepository.findOrdensPagamentoByOrdemPagamentoAgrupadoId).toHaveBeenCalledWith(ordemPagamentoAgrupadoId);
+ });
+ });
+});
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/service/ordem-pagamento.service.ts b/src/cnab/novo-remessa/service/ordem-pagamento.service.ts
new file mode 100644
index 000000000..fec29fe28
--- /dev/null
+++ b/src/cnab/novo-remessa/service/ordem-pagamento.service.ts
@@ -0,0 +1,124 @@
+import { HttpException, Injectable } from '@nestjs/common';
+import { BigqueryOrdemPagamentoDTO } from 'src/bigquery/dtos/bigquery-ordem-pagamento.dto';
+import { BigqueryOrdemPagamentoService } from 'src/bigquery/services/bigquery-ordem-pagamento.service';
+import { UsersService } from 'src/users/users.service';
+import { CustomLogger } from 'src/utils/custom-logger';
+import { OrdemPagamentoRepository } from '../repository/ordem-pagamento.repository';
+import { BigQueryToOrdemPagamento } from '../convertTo/bigquery-to-ordem-pagamento.convert';
+import { User } from 'src/users/entities/user.entity';
+import { OrdemPagamentoSemanalDto } from '../dto/ordem-pagamento-semanal.dto';
+import { OrdemPagamentoMensalDto } from '../dto/ordem-pagamento-mensal.dto';
+import { OrdemPagamentoPendenteDto } from '../dto/ordem-pagamento-pendente.dto';
+import { OrdemPagamentoPendenteNuncaRemetidasDto } from '../dto/ordem-pagamento-pendente-nunca-remetidas.dto';
+import { OrdemPagamentoAgrupadoMensalDto } from '../dto/ordem-pagamento-agrupado-mensal.dto';
+import { replaceUndefinedWithNull } from '../../../utils/type-utils';
+import { endOfDay, startOfDay } from 'date-fns';
+
+@Injectable()
+export class OrdemPagamentoService {
+ private logger = new CustomLogger(OrdemPagamentoService.name, { timestamp: true });
+
+ constructor(private ordemPagamentoRepository: OrdemPagamentoRepository, private bigqueryOrdemPagamentoService: BigqueryOrdemPagamentoService, private usersService: UsersService) {}
+
+ async sincronizarOrdensPagamento(dataCapturaInicialDate: Date, dataCapturaFinalDate: Date, consorcio: string[]) {
+ const METHOD = 'sincronizarOrdensPagamento';
+ const ordens = await this.bigqueryOrdemPagamentoService.getFromWeek(dataCapturaInicialDate, dataCapturaFinalDate, 0, { consorcioName: consorcio });
+
+ const numOrdensSemana = await this.findNumeroDeOrdensPorIntervalo(startOfDay(dataCapturaInicialDate), endOfDay(dataCapturaFinalDate));
+ // Verifica se a ultima data de captura é igual a data atual
+ // E se o número de ordens é diferente.
+ if (numOrdensSemana === ordens.length) {
+ this.logger.log(`Já foi feita a captura de ordens de pagamento para o dia de hoje.`, METHOD);
+ return;
+ }
+
+ this.logger.debug(`Iniciando sincronismo de ${ordens.length} ordens`, METHOD);
+
+ for (const ordem of ordens) {
+ let user: User | undefined;
+ if (ordem.operadoraCpfCnpj) {
+ try {
+ /*
+ Caso sejam modais, obtemos o usuário pelo CPF/CNPJ.
+ Caso contrário, obtemos pelo idConsorcio === permitCode
+ */
+ if (ordem.consorcio === 'STPC' || ordem.consorcio === 'STPL' || ordem.consorcio === 'TEC') {
+ user = await this.usersService.getOne({ cpfCnpj: ordem.operadoraCpfCnpj });
+ } else {
+ user = await this.usersService.getOne({ permitCode: ordem.idConsorcio });
+ }
+ if (user) {
+ this.logger.debug(`Salvando a ordem: ${ordem.idOrdemPagamento} para usuario: ${user.fullName}`, METHOD);
+ await this.save(ordem, user.id);
+ }
+ } catch (error) {
+ /*** TODO: Caso o erro lançado seja relacionado ao fato do usuário não ter sido encontrado,
+ ajustar o código para inserir a ordem de pagamento com o usuário nulo
+ ***/
+ if (error instanceof HttpException && !user) {
+ await this.save(ordem, undefined);
+ } else {
+ this.logger.error(`Erro ao sincronizar ordem de pagamento ${ordem.id}: ${error.message}`, METHOD);
+ }
+ }
+ }
+ }
+ this.logger.debug(`Sincronizado ${ordens.length} ordens`, METHOD);
+ }
+
+ async save(ordem: BigqueryOrdemPagamentoDTO, userId: number | undefined) {
+ const ordemPagamento = BigQueryToOrdemPagamento.convert(ordem, userId);
+ await this.ordemPagamentoRepository.save(ordemPagamento);
+ }
+
+ async findOrdensPagamentoAgrupadasPorMes(userId: number, yearMonth: Date): Promise {
+ const ordensDoMes = await this.ordemPagamentoRepository.findOrdensPagamentoAgrupadasPorMes(userId, yearMonth);
+ const ordemPagamentoMensal = new OrdemPagamentoMensalDto();
+ ordemPagamentoMensal.ordens = ordensDoMes.map((ordem) => {
+ const o = new OrdemPagamentoAgrupadoMensalDto();
+ o.ordemPagamentoAgrupadoId = ordem.ordemPagamentoAgrupadoId;
+ o.motivoStatusRemessa = ordem.motivoStatusRemessa;
+ o.valorTotal = ordem.valorTotal;
+ o.statusRemessa = ordem.statusRemessa;
+ o.descricaoMotivoStatusRemessa = ordem.descricaoMotivoStatusRemessa;
+ o.descricaoStatusRemessa = ordem.descricaoStatusRemessa;
+ o.data = ordem.data;
+ replaceUndefinedWithNull(o);
+ return o;
+ });
+ ordemPagamentoMensal.valorTotal = ordensDoMes.reduce((acc, ordem) => acc + (ordem.valorTotal || 0), 0);
+ ordemPagamentoMensal.valorTotalPago = ordensDoMes.reduce((acc, ordem) => {
+ if (ordem.motivoStatusRemessa &&
+ (ordem.motivoStatusRemessa.toString() === '00' || ordem.motivoStatusRemessa.toString() === 'BD')) {
+ return acc + (ordem.valorTotal || 0);
+ }
+ return acc;
+ }, 0);
+ return ordemPagamentoMensal;
+ }
+
+ async findOrdensPagamentoByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId: number, userId: number): Promise {
+ return await this.ordemPagamentoRepository.findOrdensPagamentoByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId, userId);
+ }
+
+ async findOrdensPagamentoAgrupadasByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId: number, userId: number): Promise {
+ return await this.ordemPagamentoRepository.findOrdensPagamentoAgrupadasByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId, userId);
+ }
+
+ async findOrdensPagamentoPendentes(): Promise {
+ return await this.ordemPagamentoRepository.findOrdensPagamentosPendentes();
+ }
+
+ async findOrdensPagamentosPendentesQueNuncaForamRemetidas(userId?: number | undefined): Promise {
+ return await this.ordemPagamentoRepository.findOrdensPagamentosPendentesQueNuncaForamRemetidas(userId);
+ }
+
+ async findOrdensPagamentoDiasAnterioresByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId: number, userId: number): Promise {
+ return await this.ordemPagamentoRepository.findOrdensPagamentoDiasAnterioresByOrdemPagamentoAgrupadoId(ordemPagamentoAgrupadoId, userId);
+ }
+
+ async findNumeroDeOrdensPorIntervalo(startDate: Date, endDate: Date) {
+ return await this.ordemPagamentoRepository.findNumeroOrdensPorIntervaloDataCaptura(startDate, endDate);
+ }
+
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/service/remessa.service.ts b/src/cnab/novo-remessa/service/remessa.service.ts
new file mode 100644
index 000000000..99aa2c4cf
--- /dev/null
+++ b/src/cnab/novo-remessa/service/remessa.service.ts
@@ -0,0 +1,244 @@
+import { Injectable } from "@nestjs/common";
+import { CustomLogger } from "src/utils/custom-logger";
+import { OrdemPagamentoAgrupadoService } from "./ordem-pagamento-agrupado.service";
+import { HeaderArquivoService } from "src/cnab/service/pagamento/header-arquivo.service";
+import { HeaderLoteService } from "src/cnab/service/pagamento/header-lote.service";
+import { DetalheAService } from "src/cnab/service/pagamento/detalhe-a.service";
+import { DetalheBService } from "src/cnab/service/pagamento/detalhe-b.service";
+import { OrdemPagamentoAgrupadoToHeaderArquivo } from "../convertTo/ordem-pagamento-agrupado-to-header-arquivo.convert";
+import { Pagador } from "src/cnab/entity/pagamento/pagador.entity";
+import { HeaderArquivo } from "src/cnab/entity/pagamento/header-arquivo.entity";
+import { OrdemPagamentoAgrupado } from "../entity/ordem-pagamento-agrupado.entity";
+import { SettingsService } from "src/settings/settings.service";
+import { UsersService } from "src/users/users.service";
+import { HeaderLote } from "src/cnab/entity/pagamento/header-lote.entity";
+import { HeaderLoteDTO } from "src/cnab/dto/pagamento/header-lote.dto";
+import { Cnab104FormaLancamento } from "src/cnab/enums/104/cnab-104-forma-lancamento.enum";
+import { HeaderLoteToDetalheA } from "../convertTo/header-lote-to-detalhe-a.convert";
+import { DetalheAToDetalheB } from "../convertTo/detalhe-a-to-detalhe-b.convert";
+import { ICnabInfo } from "src/cnab/cnab.service";
+import { SftpService } from "src/sftp/sftp.service";
+import { HeaderArquivoStatus, HeaderName } from "src/cnab/enums/pagamento/header-arquivo-status.enum";
+import { StatusRemessaEnum } from "src/cnab/enums/novo-remessa/status-remessa.enum";
+import { isEmpty } from "class-validator";
+import { PagadorService } from "src/cnab/service/pagamento/pagador.service";
+import { OrdemPagamentoAgrupadoHistorico } from "../entity/ordem-pagamento-agrupado-historico.entity";
+import { DetalhesToCnab } from "../convertTo/detalhes-to-cnab.convert";
+import { CnabRegistros104Pgto } from "src/cnab/interfaces/cnab-240/104/pagamento/cnab-registros-104-pgto.interface";
+import { Cnab104PgtoTemplates } from "src/cnab/templates/cnab-240/104/pagamento/cnab-104-pgto-templates.const";
+import { CnabFile104PgtoDTO } from "src/cnab/interfaces/cnab-240/104/pagamento/cnab-file-104-pgto.interface";
+import { CnabHeaderLote104PgtoDTO } from "src/cnab/interfaces/cnab-240/104/pagamento/cnab-header-lote-104-pgto.interface";
+import { stringifyCnab104File } from "src/cnab/utils/cnab/cnab-104-utils";
+import { CnabHeaderArquivo104DTO } from "src/cnab/dto/cnab-240/104/cnab-header-arquivo-104.dto";
+
+@Injectable()
+export class RemessaService {
+ private logger = new CustomLogger(RemessaService.name, { timestamp: true });
+
+ constructor(
+ private ordemPagamentoAgrupadoService: OrdemPagamentoAgrupadoService,
+ private headerArquivoService: HeaderArquivoService,
+ private headerLoteService: HeaderLoteService,
+ private detalheAService: DetalheAService,
+ private detalheBService: DetalheBService,
+ private settingsService: SettingsService,
+ private userService: UsersService,
+ private sftpService: SftpService,
+ private pagadorService: PagadorService
+ ) { }
+
+ //PREPARA DADOS AGRUPADOS SALVANDO NAS TABELAS CNAB
+ public async prepararRemessa(dataInicio: Date, dataFim: Date, consorcio?: string[]) {
+ const ordens = await this.ordemPagamentoAgrupadoService.getOrdens(dataInicio, dataFim, consorcio);
+ if (ordens.length > 0) {
+ const pagador = await this.pagadorService.getOneByIdPagador(ordens[0].pagadorId)
+ if (!isEmpty(ordens)) {
+ const headerArquivo = await this.gerarHeaderArquivo(pagador, this.getHeaderName(consorcio));
+ let nsrTed = 1;
+ let nsrCC = 1;
+ for (let i = 0; i < ordens.length; i++) {
+ const op = await this.ordemPagamentoAgrupadoService.getOrdemPagamento(ordens[i].id);
+ if (op != null) {
+ const user = await this.userService.getOne({ id: op.userId });
+ if (user.bankCode) {
+ const headerLote = await this.gerarHeaderLote(headerArquivo, pagador, user.bankCode);
+ let detB;
+ if (headerLote) {
+ if (headerLote.formaLancamento === '41') {
+ detB = await this.gerarDetalheAB(headerLote, op.ordemPagamentoAgrupado, nsrTed);
+ if (detB !== null) {
+ this.atualizaStatusRemessa(ordens[i], StatusRemessaEnum.PreparadoParaEnvio);
+ this.logger.debug(`Remessa preparado para: ${user.fullName} - TED`);
+ nsrTed = detB.nsr + 1;
+ }
+ } else {
+ detB = await this.gerarDetalheAB(headerLote, op.ordemPagamentoAgrupado, nsrCC);
+ if (detB !== null) {
+ this.atualizaStatusRemessa(ordens[i], StatusRemessaEnum.PreparadoParaEnvio);
+ this.logger.debug(`Remessa preparado para: ${user.fullName} - CC`);
+ nsrCC = detB.nsr + 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //PEGA INFORMAÇÕS DAS TABELAS CNAB E GERA O TXT PARA ENVIAR PARA O BANCO
+ public async gerarCnabText(headerName: HeaderName): Promise {
+ const headerArquivo = await this.headerArquivoService.getExists(HeaderArquivoStatus._2_remessaGerado, headerName);
+ if (headerArquivo[0]!==null && headerArquivo[0] !== undefined) {
+ const headerArquivoCnab = CnabHeaderArquivo104DTO.fromDTO(headerArquivo[0]);
+ return await this.gerarListaCnab(headerArquivoCnab, headerArquivo[0])
+ }
+ return [];
+ }
+
+ private async gerarListaCnab(headerArquivoCnab, headerArquivo: HeaderArquivo) {
+ const listCnab: ICnabInfo[] = [];
+
+ const trailerArquivo104 = structuredClone(Cnab104PgtoTemplates.file104.registros.trailerArquivo);
+
+ const registros: CnabRegistros104Pgto[] = [];
+
+ const headersLote = await this.headerLoteService.findAll(headerArquivo.id);
+
+ for (let i = 0; i < headersLote.length; i++) {
+
+ const detalhesA = await this.detalheAService.getDetalheAHeaderLote(headersLote[i].id);
+
+ for (let index = 0; index < detalhesA.length; index++) {
+
+ this.logger.debug(`NSR: ${detalhesA[index].nsr}`)
+
+ const historico = await this.ordemPagamentoAgrupadoService.getHistoricosOrdemDetalheA(detalhesA[index].id);
+
+ this.logger.debug(`BANK: ${historico.userBankCode} - ${historico.username}`)
+
+ const detalheB = await this.detalheBService.findDetalheBDetalheAId(detalhesA[index].id);
+
+ const detalhes = await DetalhesToCnab.convert(detalhesA[index], detalheB, historico);
+
+ registros.push(detalhes);
+
+ }
+ }
+
+ const cnab104 = new CnabFile104PgtoDTO({
+ headerArquivo: headerArquivoCnab,
+ lotes: headersLote.map((headerLote: HeaderLote) => ({
+ headerLote: CnabHeaderLote104PgtoDTO.convert(headerLote, headerArquivo),
+ registros: headerLote.formaLancamento === '41' ?//TED ou CC
+ registros.filter(r => (r.detalheA.codigoBancoDestino.value !== '104')) : //Diferente de Caixa
+ registros.filter(r => (r.detalheA.codigoBancoDestino.value === '104')),
+ trailerLote: structuredClone(Cnab104PgtoTemplates.file104.registros.trailerLote),
+ })),
+ trailerArquivo: trailerArquivo104,
+ });
+
+ if (cnab104) {
+ const [cnabStr] = stringifyCnab104File(cnab104, true, 'CnabPgtoRem');
+ if (!cnabStr) {
+ this.logger.warn(`Não foi gerado um cnabString - headerArqId: ${headerArquivo.id}`);
+ }
+ listCnab.push({ name: '', content: cnabStr, headerArquivo: headerArquivo });
+ }
+ return listCnab;
+ }
+
+
+ //PEGA O ARQUIVO TXT GERADO E ENVIA PARA O SFTP
+ public async enviarRemessa(listCnab: ICnabInfo[]) {
+ for (const cnab of listCnab) {
+ cnab.name = await this.sftpService.submitCnabRemessa(cnab.content);
+ const remessaName = ((l = cnab.name.split('/')) => l.slice(l.length - 1)[0])();
+ await this.headerArquivoService.save({
+ id: cnab.headerArquivo.id, remessaName,
+ status: HeaderArquivoStatus._3_remessaEnviado
+ });
+ }
+ }
+
+ private async gerarHeaderArquivo(pagador: Pagador, remessaName: HeaderName) {
+ let headerArquivoExists =
+ await this.headerArquivoService.getExists(HeaderArquivoStatus._2_remessaGerado, remessaName)
+ if (isEmpty(headerArquivoExists) || headerArquivoExists[0] === undefined) {
+ const nsa = await this.settingsService.getNextNSA(false);
+ const convertToHeader = OrdemPagamentoAgrupadoToHeaderArquivo.convert(pagador, nsa, remessaName);
+ return await this.headerArquivoService.save(convertToHeader);
+ }
+ return headerArquivoExists[0];
+ }
+
+ private async gerarHeaderLote(headerArquivo: HeaderArquivo, pagador: Pagador, bankCode: number) {
+ //verifica se existe header lote para o convenio para esse header arquivo
+ const formaLancamento = (bankCode === 104) ? Cnab104FormaLancamento.CreditoContaCorrente :
+ Cnab104FormaLancamento.TED;
+
+ const headersLote = await this.headerLoteService.findByFormaLancamento(headerArquivo.id, formaLancamento);
+
+ if (!isEmpty(headersLote) && headersLote.length > 0) {
+ var headerLote = headersLote.filter((h: { formaLancamento: Cnab104FormaLancamento; }) =>
+ h.formaLancamento === formaLancamento);
+
+ if (headerLote) {
+ return headerLote[0];
+ } else {
+ return await this.criarHeaderLote(headerArquivo, pagador, formaLancamento)
+ }
+ } else {
+ return await this.criarHeaderLote(headerArquivo, pagador, formaLancamento)
+ }
+ }
+
+ private async gerarDetalheAB(headerLote: HeaderLote, ordem: OrdemPagamentoAgrupado, nsr: number) {
+ const ultimoHistorico = ordem.ordensPagamentoAgrupadoHistorico[ordem.ordensPagamentoAgrupadoHistorico.length - 1];
+ const detalheA = await this.existsDetalheA(ultimoHistorico)
+
+ const numeroDocumento = await this.detalheAService.getNextNumeroDocumento(new Date());
+
+ const detalheADTO = await HeaderLoteToDetalheA.convert(headerLote, ordem, nsr, ultimoHistorico, numeroDocumento);
+
+ if (detalheA.length > 0) {
+ detalheADTO.id = detalheA[0].id;
+ detalheADTO.valorRealEfetivado = detalheA[0].valorLancamento;
+ }
+
+ const detalheASavesd = await this.detalheAService.save(detalheADTO);
+ if (detalheASavesd) {
+ const detalheB = DetalheAToDetalheB.convert(detalheASavesd, ordem);
+ return await this.detalheBService.save(detalheB);
+ }
+ return null;
+ }
+
+ private async atualizaStatusRemessa(opa: OrdemPagamentoAgrupado, statusRemessa: StatusRemessaEnum) {
+ const historico = await this.ordemPagamentoAgrupadoService.getHistoricosOrdem(opa.id);
+ if (historico) {
+ this.ordemPagamentoAgrupadoService.saveStatusHistorico(historico[historico.length - 1], statusRemessa);
+ }
+ }
+
+ private getHeaderName(consorcio: string[] | undefined): HeaderName {
+ if (['VLT'].some(i => consorcio?.includes(i))) {
+ return HeaderName.VLT;
+ } else if (['STPC', 'STPL', 'TEC'].some(i => consorcio?.includes(i))) {
+ return HeaderName.MODAL;
+ } else {
+ return HeaderName.CONSORCIO;
+ }
+ }
+
+ private async criarHeaderLote(headerArquivo: HeaderArquivo, pagador: Pagador, formaPagamento: Cnab104FormaLancamento) {
+ var headerLoteNew = HeaderLoteDTO.fromHeaderArquivoDTO(headerArquivo, pagador, formaPagamento);
+ return await this.headerLoteService.saveDto(headerLoteNew);
+ }
+
+ async existsDetalheA(ultimoHistorico: OrdemPagamentoAgrupadoHistorico) {
+ return await this.detalheAService.existsDetalheA(ultimoHistorico.id)
+
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/novo-remessa/service/retorno.service.ts b/src/cnab/novo-remessa/service/retorno.service.ts
new file mode 100644
index 000000000..a179b6084
--- /dev/null
+++ b/src/cnab/novo-remessa/service/retorno.service.ts
@@ -0,0 +1,83 @@
+import { Injectable } from "@nestjs/common";
+import { DetalheAService } from "src/cnab/service/pagamento/detalhe-a.service";
+import { SftpService } from "src/sftp/sftp.service";
+import { CustomLogger } from "src/utils/custom-logger";
+import { OrdemPagamentoAgrupadoService } from "./ordem-pagamento-agrupado.service";
+import { parseCnab240Pagamento } from "src/cnab/utils/cnab/cnab-104-utils";
+import { StatusRemessaEnum } from "src/cnab/enums/novo-remessa/status-remessa.enum";
+import { DetalheA } from "src/cnab/entity/pagamento/detalhe-a.entity";
+import { CnabRegistros104Pgto } from "src/cnab/interfaces/cnab-240/104/pagamento/cnab-registros-104-pgto.interface";
+import { CnabLote104Pgto } from "src/cnab/interfaces/cnab-240/104/pagamento/cnab-lote-104-pgto.interface";
+
+@Injectable()
+export class RetornoService {
+ private logger = new CustomLogger(RetornoService.name, { timestamp: true });
+
+ constructor(
+ private ordemPagamentoAgrupadoService: OrdemPagamentoAgrupadoService,
+ private detalheAService: DetalheAService,
+ private sftpService: SftpService,
+ ) { }
+
+ //LER ARQUIVO TXT CNAB DO SFTP
+ public async lerRetornoSftp(folder?: string) {
+ return await this.sftpService.getFirstRetornoPagamento(folder);
+ }
+
+ public async salvarRetorno(cnab: { name: string, content: string }) {
+ this.logger.debug(`Iniciada a leitura do arquivo: ${cnab.name} - ${new Date()}`);
+ const retorno104 = parseCnab240Pagamento(cnab.content);
+
+ for (const cnabLote of retorno104.lotes) {
+ for (const registro of cnabLote.registros) {
+ const detalheA = await this.detalheAService.getOne({
+ dataVencimento: registro.detalheA.dataVencimento.value,
+ valorLancamento: registro.detalheA.valorLancamento.value,
+ ordemPagamentoAgrupadoHistorico: {
+ userBankCode: registro.detalheA.codigoBancoDestino.value,
+ userBankAgency: registro.detalheA.codigoAgenciaDestino.value,
+ userBankAccount: registro.detalheA.contaCorrenteDestino.value,
+ statusRemessa: StatusRemessaEnum.PreparadoParaEnvio | StatusRemessaEnum.AguardandoPagamento
+ }
+ })
+ this.atualizarStatusRemessaHistorico(cnabLote, registro, detalheA);
+ }
+ }
+ }
+
+ private async atualizarStatusRemessaHistorico(
+ cnabLote: CnabLote104Pgto, registro: CnabRegistros104Pgto, detalheA: DetalheA
+ ) {
+ if (detalheA && detalheA.ordemPagamentoAgrupadoHistorico) {
+ if (detalheA.ordemPagamentoAgrupadoHistorico.statusRemessa === StatusRemessaEnum.PreparadoParaEnvio) {
+ await this.ordemPagamentoAgrupadoService.saveStatusHistorico(
+ detalheA.ordemPagamentoAgrupadoHistorico,
+ StatusRemessaEnum.AguardandoPagamento
+ );
+ } else if (detalheA.ordemPagamentoAgrupadoHistorico.statusRemessa === StatusRemessaEnum.AguardandoPagamento) {
+ detalheA.ordemPagamentoAgrupadoHistorico.dataReferencia = new Date();
+ detalheA.ordemPagamentoAgrupadoHistorico.motivoStatusRemessa =
+ registro.detalheA.ocorrencias.value;
+ //SE O HEADER LOTE ESTIVER COM ERRO TODOS OS DETALHES FICAM COMO NÃO EFETIVADOS
+ if (!cnabLote.headerLote.ocorrencias.value.in('BD', '00')) {
+ detalheA.ordemPagamentoAgrupadoHistorico.motivoStatusRemessa =
+ cnabLote.headerLote.ocorrencias.value;
+ await this.ordemPagamentoAgrupadoService.saveStatusHistorico(
+ detalheA.ordemPagamentoAgrupadoHistorico,
+ StatusRemessaEnum.NaoEfetivado
+ )
+ } else if (registro.detalheA.ocorrencias.value.in('BD', '00')) {
+ await this.ordemPagamentoAgrupadoService.saveStatusHistorico(
+ detalheA.ordemPagamentoAgrupadoHistorico,
+ StatusRemessaEnum.Efetivado
+ )
+ } else {
+ await this.ordemPagamentoAgrupadoService.saveStatusHistorico(
+ detalheA.ordemPagamentoAgrupadoHistorico,
+ StatusRemessaEnum.NaoEfetivado
+ );
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/cnab/repository/cliente-favorecido.repository.ts b/src/cnab/repository/cliente-favorecido.repository.ts
index 6825de8db..c0124c9eb 100644
--- a/src/cnab/repository/cliente-favorecido.repository.ts
+++ b/src/cnab/repository/cliente-favorecido.repository.ts
@@ -14,6 +14,8 @@ export interface IClienteFavorecidoRawWhere {
cpfCnpj?: string;
/** numeroDocumentoEmpresa */
detalheANumeroDocumento?: number[];
+ dataVencimento?: Date[];
+ valorLancamento?: number[];
}
export interface IClienteFavorecidoFindBy {
@@ -158,38 +160,73 @@ export class ClienteFavorecidoRepository {
}
public async findManyRaw(where: IClienteFavorecidoRawWhere): Promise {
- const qWhere: { query: string; params?: any[] } = { query: '' };
+ let query = `
+ SELECT cf.*
+ FROM cliente_favorecido cf
+ ${where.detalheANumeroDocumento || where.dataVencimento || where.valorLancamento
+ ? `
+ INNER JOIN item_transacao it ON it."clienteFavorecidoId" = cf.id
+ INNER JOIN item_transacao_agrupado ita ON ita.id = it."itemTransacaoAgrupadoId"
+ INNER JOIN detalhe_a da ON da."itemTransacaoAgrupadoId" = ita.id
+ `
+ : ''
+ }
+ WHERE 1=1
+ `;
+
if (where.id) {
- qWhere.query = 'WHERE cf.id IN ($1)';
- qWhere.params = [where.id];
- } else if (where.cpfCnpj) {
- qWhere.query = `WHERE cf."cpfCnpj" = $1`;
- qWhere.params = [where.cpfCnpj];
- } else if (where.nome) {
- const nomes = where.nome.map((n) => `'%${n}%'`);
- qWhere.query = `WHERE cf.nome ILIKE ANY(ARRAY[${nomes.join(',')}])`;
- qWhere.params = [];
- } else if (where.detalheANumeroDocumento) {
- qWhere.query = `WHERE da."numeroDocumentoEmpresa" IN (${where.detalheANumeroDocumento.join(',')})`;
- qWhere.params = [];
+ query += ` AND cf.id IN (${where.id.map((id) => `'${id}'`).join(',')})`;
+ }
+
+ if (where.cpfCnpj) {
+ query += ` AND cf."cpfCnpj" = '${where.cpfCnpj}'`;
}
- const result: any[] = await this.clienteFavorecidoRepository.query(
- compactQuery(`
- SELECT cf.*
- FROM cliente_favorecido cf
- ${
- where?.detalheANumeroDocumento
- ? `INNER JOIN item_transacao it ON it."clienteFavorecidoId" = cf.id
- INNER JOIN item_transacao_agrupado ita ON ita.id = it."itemTransacaoAgrupadoId"
- INNER JOIN detalhe_a da ON da."itemTransacaoAgrupadoId" = ita.id`
- : ''
+
+ if (where.nome) {
+ const trimmedNames = where.nome.map((n) => n.trim());
+ const nomes = trimmedNames.map((n) => `'%${n}%'`);
+ query += ` AND cf.nome ILIKE ANY(ARRAY[${nomes.join(',')}])`;
+
+ const containsCorsorcio = trimmedNames.some(
+ (name) => name.toLowerCase().includes('concessionaria') || name.toLowerCase().includes('consorcio')
+ );
+ if (containsCorsorcio && where.detalheANumeroDocumento) {
+ query += ` AND da."numeroDocumentoEmpresa" IN (${where.detalheANumeroDocumento
+ .map((doc) => `'${doc}'`)
+ .join(',')})`;
}
- ${qWhere.query}
- ORDER BY cf.id
- `),
- qWhere.params,
- );
- const itens = result.map((i) => new ClienteFavorecido(i));
- return itens;
+ }
+ if (where.detalheANumeroDocumento) {
+ query += ` AND da."numeroDocumentoEmpresa" IN (${where.detalheANumeroDocumento
+ .map((doc) => `'${doc}'`)
+ .join(',')})`;
+ }
+
+ if (where.dataVencimento) {
+ const normalizedDates = where.dataVencimento.map((date) => {
+ const parsedDate = new Date(date);
+ if (isNaN(parsedDate.getTime())) {
+ throw new Error(`Invalid date format: ${date}`);
+ }
+ return parsedDate.toISOString();
+ });
+ query += ` AND da."dataVencimento" = ANY(ARRAY[${normalizedDates
+ .map((date) => `'${date}'`)
+ .join(',')}]::timestamp[])`;
+ }
+
+ if (where.valorLancamento) {
+ query += ` AND da."valorLancamento" IN (${where.valorLancamento
+ .map((valor) => `${valor}`)
+ .join(',')})`;
+ }
+
+ query += ` ORDER BY cf.id`;
+
+ const result: any[] = await this.clienteFavorecidoRepository.query(compactQuery(query));
+
+ return result.map((i) => new ClienteFavorecido(i));
}
+
+
}
diff --git a/src/cnab/repository/pagamento/detalhe-a.repository.ts b/src/cnab/repository/pagamento/detalhe-a.repository.ts
index 7688d4688..df076d95f 100644
--- a/src/cnab/repository/pagamento/detalhe-a.repository.ts
+++ b/src/cnab/repository/pagamento/detalhe-a.repository.ts
@@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { endOfDay, startOfDay } from 'date-fns';
import { EntityCondition } from 'src/utils/types/entity-condition.type';
import { Nullable } from 'src/utils/types/nullable.type';
-import { DeepPartial, FindManyOptions, FindOneOptions, InsertResult, LessThanOrEqual, MoreThanOrEqual, Repository } from 'typeorm';
+import { DataSource, DeepPartial, FindManyOptions, FindOneOptions, InsertResult, LessThanOrEqual, MoreThanOrEqual, Repository } from 'typeorm';
import { compactQuery } from 'src/utils/console-utils';
import { CommonHttpException } from 'src/utils/http-exception/common-http-exception';
@@ -15,24 +15,29 @@ export interface IDetalheARawWhere {
itemTransacaoAgrupado?: { id: number[] };
numeroDocumentoEmpresa?: number;
headerLote?: { id: number[] };
+ nome?: string[];
+ dataVencimento?: Date[];
+ valorLancamento?: number[];
}
@Injectable()
export class DetalheARepository {
+
private logger: Logger = new Logger('DetalheARepository', { timestamp: true });
constructor(
@InjectRepository(DetalheA)
private detalheARepository: Repository,
- ) {}
+ private readonly dataSource: DataSource
+ ) { }
public insert(dtos: DeepPartial[]): Promise {
return this.detalheARepository.insert(dtos);
}
public async save(dto: DeepPartial): Promise {
- const saved = await this.detalheARepository.save(dto);
- return await this.getOneRaw({ id: [saved.id] });
+ return await this.detalheARepository.save(dto);
+ // return await this.getOneRaw({ id: [saved.id] });
}
public async getOne(fields: EntityCondition): Promise {
@@ -42,18 +47,57 @@ export class DetalheARepository {
}
public async findRaw(where: IDetalheARawWhere): Promise {
- const qWhere: { query: string; params?: any[] } = { query: '' };
+ const qWhere: { query: string; params: any[] } = { query: 'WHERE 1=1', params: [] };
+
if (where.id) {
- qWhere.query = `WHERE da.id IN (${where.id.join(',')})`;
- qWhere.params = [];
- } else if (where.numeroDocumentoEmpresa) {
- qWhere.query = `WHERE da."numeroDocumentoEmpresa" = $1`;
- qWhere.params = [where.numeroDocumentoEmpresa];
- } else if (where.headerLote) {
- qWhere.query = `WHERE hl.id IN(${where.headerLote.id.join(',')})`;
- } else if (where.nsr && where.itemTransacaoAgrupado) {
- qWhere.query = `WHERE da.nsr IN(${where.nsr.join(',')}) AND da."itemTransacaoAgrupadoId" IN(${where.itemTransacaoAgrupado.id.join(',')})`;
+ qWhere.query += ` AND da.id IN (${where.id.join(',')})`;
+ }
+
+ if (where.numeroDocumentoEmpresa) {
+ qWhere.query += ` AND da."numeroDocumentoEmpresa" = $${qWhere.params.length + 1}`;
+ qWhere.params.push(where.numeroDocumentoEmpresa);
+ }
+
+ if (where.headerLote) {
+ qWhere.query += ` AND hl.id IN (${where.headerLote.id.join(',')})`;
+ }
+ if (where.nsr && where.itemTransacaoAgrupado) {
+ qWhere.query += ` AND da.nsr IN (${where.nsr.join(',')}) AND da."itemTransacaoAgrupadoId" IN (${where.itemTransacaoAgrupado.id.join(',')})`;
}
+
+ if (where.dataVencimento) {
+ const normalizedDates = where.dataVencimento.map((date) => {
+ const parsedDate = new Date(date);
+ if (isNaN(parsedDate.getTime())) {
+ throw new Error(`Invalid date format: ${date}`);
+ }
+ return parsedDate.toISOString();
+ });
+ qWhere.query += ` AND da."dataVencimento" = ANY(ARRAY[${normalizedDates
+ .map((date) => `'${date}'`)
+ .join(',')}]::timestamp[])`;
+ }
+
+ if (where.valorLancamento) {
+ qWhere.query += ` AND da."valorLancamento" IN (${where.valorLancamento
+ .map((valor) => `${valor}`)
+ .join(',')})`;
+ }
+
+ if (where.nome) {
+ const trimmedNames = where.nome.map((n) => n.trim());
+ const nomes = trimmedNames.map((n) => `'%${n}%'`);
+ qWhere.query += ` AND cf.nome ILIKE ANY(ARRAY[${nomes.join(',')}])`;
+
+ const containsCorsorcio = trimmedNames.some(
+ (name) => name.toLowerCase().includes('concessionaria') || name.toLowerCase().includes('consorcio')
+ );
+ if (containsCorsorcio && where.numeroDocumentoEmpresa) {
+ qWhere.query += ` AND da."numeroDocumentoEmpresa" = $${qWhere.params.length + 1}`;
+ qWhere.params.push(where.numeroDocumentoEmpresa);
+ }
+ }
+
const result: any[] = await this.detalheARepository.query(
compactQuery(`
SELECT
@@ -81,6 +125,8 @@ export class DetalheARepository {
INNER JOIN item_transacao_agrupado ita ON da."itemTransacaoAgrupadoId" = ita.id
INNER JOIN transacao_agrupado ta ON ta.id = ita."transacaoAgrupadoId"
INNER JOIN transacao_status ts ON ts.id = ta."statusId"
+ INNER JOIN item_transacao it on ita.id = it."itemTransacaoAgrupadoId"
+ INNER JOIN cliente_favorecido cf on it."clienteFavorecidoId" = cf.id
${qWhere.query}
ORDER BY da.id
`),
@@ -90,6 +136,45 @@ export class DetalheARepository {
return detalhes;
}
+ async existsDetalheA(id: number) {
+ const query = (`select da.* from detalhe_a da
+ inner join ordem_pagamento_agrupado_historico oph
+ on da."ordemPagamentoAgrupadoHistoricoId"= oph.id
+ where da."ordemPagamentoAgrupadoHistoricoId"=${id} `)
+
+ const queryRunner = this.dataSource.createQueryRunner();
+
+ queryRunner.connect();
+
+ const result: any[] = await queryRunner.query(query);
+
+ const detalhes = result.map((i) => new DetalheA(i));
+
+ queryRunner.release()
+
+ return detalhes;
+
+ }
+
+ async getDetalheAHeaderLote(id: number) {
+
+ const query = (`select da.* from detalhe_a da where da."headerLoteId" = ${id}
+ order by da."nsr" asc `)
+
+ const queryRunner = this.dataSource.createQueryRunner();
+
+ queryRunner.connect();
+
+ const result: any[] = await queryRunner.manager.getRepository(DetalheA).query(query);
+
+ const detalhes = result.map((i) => new DetalheA(i));
+
+ queryRunner.release()
+
+ return detalhes;
+
+ }
+
public async findOneRaw(where: IDetalheARawWhere): Promise {
const result = await this.findRaw(where);
if (result.length == 0) {
diff --git a/src/cnab/repository/pagamento/detalhe-b.repository.ts b/src/cnab/repository/pagamento/detalhe-b.repository.ts
index eb2a3baba..ff38a9ded 100644
--- a/src/cnab/repository/pagamento/detalhe-b.repository.ts
+++ b/src/cnab/repository/pagamento/detalhe-b.repository.ts
@@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EntityCondition } from 'src/utils/types/entity-condition.type';
import { Nullable } from 'src/utils/types/nullable.type';
-import { DeepPartial, FindManyOptions, In, InsertResult, Repository } from 'typeorm';
+import { DataSource, DeepPartial, FindManyOptions, In, InsertResult, Repository } from 'typeorm';
import { DetalheBDTO } from '../../dto/pagamento/detalhe-b.dto';
import { DetalheB } from '../../entity/pagamento/detalhe-b.entity';
import { SaveIfNotExists } from 'src/utils/types/save-if-not-exists.type';
@@ -19,6 +19,7 @@ export class DetalheBRepository {
constructor(
@InjectRepository(DetalheB)
private detalheBRepository: Repository,
+ private dataSource: DataSource
) { }
/**
@@ -94,4 +95,22 @@ export class DetalheBRepository {
public async findMany(options?: FindManyOptions): Promise {
return await this.detalheBRepository.find(options);
}
+
+ async getDetalheBDetalheAId(detalheAId: number) {
+
+ const query = (`select ddb.* from detalhe_b ddb where ddb."detalheAId" = ${detalheAId}`)
+
+ const queryRunner = this.dataSource.createQueryRunner();
+
+ queryRunner.connect();
+
+ const result: any[] = await queryRunner.manager.getRepository(DetalheB).query(query);
+
+ const detalheB = result.map((i) => new DetalheB(i));
+
+ queryRunner.release()
+
+ return detalheB[0];
+
+ }
}
diff --git a/src/cnab/repository/pagamento/header-arquivo.repository.ts b/src/cnab/repository/pagamento/header-arquivo.repository.ts
index c2f5ea18d..7d24b14e0 100644
--- a/src/cnab/repository/pagamento/header-arquivo.repository.ts
+++ b/src/cnab/repository/pagamento/header-arquivo.repository.ts
@@ -5,6 +5,7 @@ import { logWarn } from 'src/utils/log-utils';
import { EntityCondition } from 'src/utils/types/entity-condition.type';
import { SaveIfNotExists } from 'src/utils/types/save-if-not-exists.type';
import {
+ DataSource,
DeepPartial,
FindManyOptions,
FindOneOptions,
@@ -25,6 +26,7 @@ export class HeaderArquivoRepository {
constructor(
@InjectRepository(HeaderArquivo)
private headerArquivoRepository: Repository,
+ private readonly dataSource: DataSource,
) {}
public async save(dto: DeepPartial): Promise {
@@ -134,4 +136,14 @@ export class HeaderArquivoRepository {
): Promise {
return await this.headerArquivoRepository.find(options);
}
+
+ public async getHeaderArquivo(status:string,remessaName:string): Promise{
+ const query = (`select ha.* from header_arquivo ha where ha."status" ='${status}'
+ and ha."remessaName" ='${remessaName}' `);
+ const queryRunner = this.dataSource.createQueryRunner();
+ await queryRunner.connect();
+ let result: any = await queryRunner.query(query);
+ queryRunner.release();
+ return result.map((r: DeepPartial | undefined) => new HeaderArquivo(r));
+ }
}
diff --git a/src/cnab/repository/pagamento/header-lote.repository.ts b/src/cnab/repository/pagamento/header-lote.repository.ts
index 3a4be1570..5042d97c5 100644
--- a/src/cnab/repository/pagamento/header-lote.repository.ts
+++ b/src/cnab/repository/pagamento/header-lote.repository.ts
@@ -6,6 +6,7 @@ import { EntityCondition } from 'src/utils/types/entity-condition.type';
import { Nullable } from 'src/utils/types/nullable.type';
import { SaveIfNotExists } from 'src/utils/types/save-if-not-exists.type';
import {
+ DataSource,
DeepPartial,
FindManyOptions,
In,
@@ -16,6 +17,7 @@ import { HeaderLote } from '../../entity/pagamento/header-lote.entity';
@Injectable()
export class HeaderLoteRepository {
+
private logger: Logger = new Logger('HeaderLoteRepository', {
timestamp: true,
});
@@ -23,6 +25,7 @@ export class HeaderLoteRepository {
constructor(
@InjectRepository(HeaderLote)
private headerLoteRepository: Repository,
+ private readonly dataSource: DataSource
) {}
/**
@@ -129,4 +132,34 @@ export class HeaderLoteRepository {
): Promise {
return await this.headerLoteRepository.find(options);
}
+
+ public async findAll(headerArquivoId: number) {
+ const query = (`select hl.* from header_lote hl where hl."headerArquivoId"=${headerArquivoId}`)
+
+ const queryRunner = this.dataSource.createQueryRunner();
+
+ queryRunner.connect();
+
+ let result: any = await queryRunner.query(query);
+
+ queryRunner.release();
+
+ return result.map((r: DeepPartial | undefined) => new HeaderLote(r));
+ }
+
+
+ public async findByFormaLancamento(headerArquivoId: number, formaLancamento:string) {
+ const query = (`select hl.* from header_lote hl where hl."headerArquivoId"=${headerArquivoId}
+ and hl."formaLancamento"='${formaLancamento}'`)
+
+ const queryRunner = this.dataSource.createQueryRunner();
+
+ queryRunner.connect();
+
+ let result: any = await queryRunner.query(query);
+
+ queryRunner.release();
+
+ return result.map((r: DeepPartial | undefined) => new HeaderLote(r));
+ }
}
diff --git a/src/cnab/service/pagamento/detalhe-a.service.ts b/src/cnab/service/pagamento/detalhe-a.service.ts
index 97057132f..6e0bdd40a 100644
--- a/src/cnab/service/pagamento/detalhe-a.service.ts
+++ b/src/cnab/service/pagamento/detalhe-a.service.ts
@@ -6,8 +6,7 @@ import { CnabRegistros104Pgto } from 'src/cnab/interfaces/cnab-240/104/pagamento
import { CustomLogger } from 'src/utils/custom-logger';
import { EntityCondition } from 'src/utils/types/entity-condition.type';
import { Nullable } from 'src/utils/types/nullable.type';
-import { validateDTO } from 'src/utils/validation-utils';
-import { DeepPartial, FindOneOptions } from 'typeorm';
+import { FindOneOptions } from 'typeorm';
import { DetalheADTO } from '../../dto/pagamento/detalhe-a.dto';
import { DetalheA } from '../../entity/pagamento/detalhe-a.entity';
import { DetalheARepository, IDetalheARawWhere } from '../../repository/pagamento/detalhe-a.repository';
@@ -16,9 +15,11 @@ import { PagamentosPendentes } from './../../entity/pagamento/pagamentos-pendent
import { PagamentosPendentesService } from './pagamentos-pendentes.service';
import { TransacaoAgrupadoService } from './transacao-agrupado.service';
import { CnabHeaderArquivo104 } from 'src/cnab/dto/cnab-240/104/cnab-header-arquivo-104.dto';
+import { validateDTO } from 'src/utils/validation-utils';
@Injectable()
export class DetalheAService {
+
private logger = new CustomLogger('DetalheAService', { timestamp: true });
constructor(private detalheARepository: DetalheARepository, private clienteFavorecidoService: ClienteFavorecidoService, private transacaoAgrupadoService: TransacaoAgrupadoService, private pagamentosPendentesService: PagamentosPendentesService) {}
@@ -42,14 +43,25 @@ export class DetalheAService {
public async saveRetornoFrom104(headerArq: CnabHeaderArquivo104, headerLotePgto: CnabHeaderLote104Pgto, r: CnabRegistros104Pgto, dataEfetivacao: Date, retornoName: string): Promise {
const logRegistro = `HeaderArquivo: ${headerArq.nsa.convertedValue}, lote: ${headerLotePgto.codigoRegistro.value}`;
const favorecido = await this.clienteFavorecidoService.findOneRaw({
+ nome: [r.detalheA.nomeTerceiro.stringValue],
+ // dataVencimento: [r.detalheA.dataVencimento.convertedValue],
detalheANumeroDocumento: [r.detalheA.numeroDocumentoEmpresa.convertedValue],
+ valorLancamento: [r.detalheA.valorLancamento.convertedValue]
});
+
if (!favorecido) {
this.logger.warn(logRegistro + ` Detalhe A Documento: ${r.detalheA.numeroDocumentoEmpresa.convertedValue} - Favorecido não encontrado para o nome: '${r.detalheA.nomeTerceiro.stringValue.trim()}'`);
return 'favorecidoNotFound';
}
- const detalheA = await this.detalheARepository.findOneRaw({ numeroDocumentoEmpresa: r.detalheA.numeroDocumentoEmpresa.convertedValue });
+
+ const detalheA = await this.detalheARepository.findOneRaw({
+ nome: [r.detalheA.nomeTerceiro.stringValue],
+ // dataVencimento: [r.detalheA.dataVencimento.convertedValue],
+ numeroDocumentoEmpresa: r.detalheA.numeroDocumentoEmpresa.convertedValue,
+ valorLancamento: [r.detalheA.valorLancamento.convertedValue]
+});
+
if (!detalheA) {
this.logger.warn(logRegistro + ` Detalhe A Documento: ${r.detalheA.numeroDocumentoEmpresa.convertedValue}, favorecido: '${favorecido.nome}' - NÃO ENCONTRADO!`);
return 'detalheANotFound';
@@ -141,4 +153,12 @@ export class DetalheAService {
public async getNextNumeroDocumento(date: Date): Promise {
return await this.detalheARepository.getNextNumeroDocumento(date);
}
+
+ public async existsDetalheA(id: number) {
+ return await this.detalheARepository.existsDetalheA(id);
+ }
+
+ public async getDetalheAHeaderLote(headerLoteId: number){
+ return await this.detalheARepository.getDetalheAHeaderLote(headerLoteId)
+ }
}
diff --git a/src/cnab/service/pagamento/detalhe-b.service.ts b/src/cnab/service/pagamento/detalhe-b.service.ts
index b1a7ecb09..c90f16805 100644
--- a/src/cnab/service/pagamento/detalhe-b.service.ts
+++ b/src/cnab/service/pagamento/detalhe-b.service.ts
@@ -40,9 +40,9 @@ export class DetalheBService {
return await this.detalheBRepository.save(detalheB);
}
- public async save(dto: DetalheBDTO): Promise {
+ public async save(dto: DetalheBDTO): Promise {
await validateDTO(DetalheBDTO, dto);
- await this.detalheBRepository.save(dto);
+ return await this.detalheBRepository.save(dto);
}
public async findOne(fields: EntityCondition): Promise> {
@@ -52,4 +52,8 @@ export class DetalheBService {
public async findMany(options?: FindManyOptions): Promise {
return await this.detalheBRepository.findMany(options);
}
+
+ public async findDetalheBDetalheAId(detalheAId: number): Promise{
+ return await this.detalheBRepository.getDetalheBDetalheAId(detalheAId);
+ }
}
diff --git a/src/cnab/service/pagamento/header-arquivo.service.ts b/src/cnab/service/pagamento/header-arquivo.service.ts
index 469baece5..04df61c01 100644
--- a/src/cnab/service/pagamento/header-arquivo.service.ts
+++ b/src/cnab/service/pagamento/header-arquivo.service.ts
@@ -4,15 +4,17 @@ import { SettingsService } from 'src/settings/settings.service';
import { CustomLogger } from 'src/utils/custom-logger';
import { EntityCondition } from 'src/utils/types/entity-condition.type';
import { SaveIfNotExists } from 'src/utils/types/save-if-not-exists.type';
-import { DeepPartial, FindOptionsWhere } from 'typeorm';
+import { DeepPartial, FindOptionsWhere } from 'typeorm';
import { HeaderArquivoDTO } from '../../dto/pagamento/header-arquivo.dto';
import { HeaderArquivo } from '../../entity/pagamento/header-arquivo.entity';
import { HeaderArquivoTipoArquivo } from '../../enums/pagamento/header-arquivo-tipo-arquivo.enum';
import { HeaderArquivoRepository } from '../../repository/pagamento/header-arquivo.repository';
import { PagadorService } from './pagador.service';
+import { HeaderArquivoStatus, HeaderName } from 'src/cnab/enums/pagamento/header-arquivo-status.enum';
@Injectable()
export class HeaderArquivoService {
+
public async getHeaderArquivoNsa(index: number) {
return this.getOne({ nsa: index });
}
@@ -60,4 +62,8 @@ export class HeaderArquivoService {
public async getOne(fields: EntityCondition): Promise {
return await this.headerArquivoRepository.getOne(fields);
}
+
+ public async getExists(status: HeaderArquivoStatus, remessaName: HeaderName) {
+ return await this.headerArquivoRepository.getHeaderArquivo(status,remessaName);
+ }
}
diff --git a/src/cnab/service/pagamento/header-lote.service.ts b/src/cnab/service/pagamento/header-lote.service.ts
index f22f4d33f..3aa8337db 100644
--- a/src/cnab/service/pagamento/header-lote.service.ts
+++ b/src/cnab/service/pagamento/header-lote.service.ts
@@ -1,8 +1,5 @@
import { Injectable, Logger } from '@nestjs/common';
-import { HeaderArquivoDTO } from 'src/cnab/dto/pagamento/header-arquivo.dto';
import { HeaderArquivo } from 'src/cnab/entity/pagamento/header-arquivo.entity';
-import { Pagador } from 'src/cnab/entity/pagamento/pagador.entity';
-import { Cnab104FormaLancamento } from 'src/cnab/enums/104/cnab-104-forma-lancamento.enum';
import { CnabLote104Pgto } from 'src/cnab/interfaces/cnab-240/104/pagamento/cnab-lote-104-pgto.interface';
import { EntityCondition } from 'src/utils/types/entity-condition.type';
import { Nullable } from 'src/utils/types/nullable.type';
@@ -12,7 +9,6 @@ import { HeaderLoteDTO } from '../../dto/pagamento/header-lote.dto';
import { HeaderLote } from '../../entity/pagamento/header-lote.entity';
import { HeaderLoteRepository } from '../../repository/pagamento/header-lote.repository';
import { PagadorService } from './pagador.service';
-import { Cnab104PgtoTemplates } from 'src/cnab/templates/cnab-240/104/pagamento/cnab-104-pgto-templates.const';
@Injectable()
export class HeaderLoteService {
@@ -70,7 +66,15 @@ export class HeaderLoteService {
return await this.headerLoteRepository.findOne(fields);
}
- public async findMany(fields: EntityCondition): Promise {
+ public async findMany(fields: EntityCondition): Promise {
return await this.headerLoteRepository.findMany({ where: fields });
}
+
+ public async findAll(headerArquivoId: number){
+ return await this.headerLoteRepository.findAll(headerArquivoId)
+ }
+
+ public async findByFormaLancamento(headerArquivoId: number,formaLancamento: string){
+ return await this.headerLoteRepository.findByFormaLancamento(headerArquivoId,formaLancamento)
+ }
}
diff --git a/src/cnab/service/pagamento/pagador.service.ts b/src/cnab/service/pagamento/pagador.service.ts
index 3916f0ff0..a7b4a1d03 100644
--- a/src/cnab/service/pagamento/pagador.service.ts
+++ b/src/cnab/service/pagamento/pagador.service.ts
@@ -19,7 +19,6 @@ export class PagadorService {
return {
cett: await this.getOneByConta(PagadorContaEnum.CETT),
contaBilhetagem: await this.getOneByConta(PagadorContaEnum.ContaBilhetagem),
-
}
}
diff --git a/src/cnab/service/pagamento/remessa-retorno.service.ts b/src/cnab/service/pagamento/remessa-retorno.service.ts
index 95538ae0a..7bc35cc50 100644
--- a/src/cnab/service/pagamento/remessa-retorno.service.ts
+++ b/src/cnab/service/pagamento/remessa-retorno.service.ts
@@ -183,9 +183,10 @@ export class RemessaRetornoService {
}
async verificaPagamentoIndevido(itemTransacao: ItemTransacao) {
- if (itemTransacao.nomeConsorcio === 'STPC' || itemTransacao.nomeConsorcio === 'STPL') {
+ if (itemTransacao.nomeConsorcio === 'STPC' || itemTransacao.nomeConsorcio === 'STPL'
+ || itemTransacao.nomeConsorcio === 'TEC') {
const pagamentoIndevido = (await this.pagamentoIndevidoService.findAll())
- .filter(p => p.nomeFavorecido === itemTransacao.clienteFavorecido.nome);
+ .filter(p => p.nomeFavorecido === itemTransacao.clienteFavorecido.nome && p.saldoDevedor > 0);
if (pagamentoIndevido && pagamentoIndevido[0] !== undefined
&& pagamentoIndevido[0].saldoDevedor !== undefined) {
return pagamentoIndevido[0].saldoDevedor > 0 ? pagamentoIndevido[0] : undefined;
diff --git a/src/cnab/utils/cnab/cnab-104-utils.ts b/src/cnab/utils/cnab/cnab-104-utils.ts
index 20564c57e..469e81a51 100644
--- a/src/cnab/utils/cnab/cnab-104-utils.ts
+++ b/src/cnab/utils/cnab/cnab-104-utils.ts
@@ -109,7 +109,7 @@ function processCnab104TrailerLote(lote: CnabLote104) {
}
function getSomarioValoresCnabLote(lote: CnabLote104): number {
- const original = lote.registros.map((i) => Number(i.detalheA?.valorLancamento?.convertedValue || 0));
+ const original = lote.registros.map((i) => Number(i.detalheA?.valorLancamento?.value || 0));
const rounded = original.map((i) => Number(i.toFixed(2)));
const sum = rounded.reduce((num, sum) => sum + num, 0);
return sum;
diff --git a/src/cron-jobs/cron-jobs-manutencao.controller.ts b/src/cron-jobs/cron-jobs-manutencao.controller.ts
index 8bed8ed81..069ed552c 100644
--- a/src/cron-jobs/cron-jobs-manutencao.controller.ts
+++ b/src/cron-jobs/cron-jobs-manutencao.controller.ts
@@ -1,12 +1,8 @@
import { Controller, Get, HttpCode, HttpStatus, Query, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiBearerAuth, ApiOperation, ApiQuery, ApiTags } from '@nestjs/swagger';
-import { Roles } from 'src/roles/roles.decorator';
-import { RoleEnum } from 'src/roles/roles.enum';
-import { RolesGuard } from 'src/roles/roles.guard';
-import { ApiDescription } from 'src/utils/api-param/description-api-param';
+
import { CustomLogger } from 'src/utils/custom-logger';
-import { ParseDatePipe } from 'src/utils/pipes/parse-date.pipe';
import { CronJobsService } from './cron-jobs.service';
@ApiTags('Manutenção')
@@ -21,48 +17,5 @@ export class CronJobsManutencaoController {
private readonly cronJobsService: CronJobsService, //
) {}
- @Get('generateRemessaVLT')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta a geração e envio de remessa - exatamente como no cronjob.' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'today', type: String, required: false, description: ApiDescription({ _: "Define uma data customizada para 'hoje'", example: '2024-07-15' }) })
- @ApiQuery({ name: 'force', type: Boolean, required: false, description: 'Ignora validação de data e afins no método do cronjob, e executa mesmo assim', example: false })
- async getGenerateRemessaVLT(
- @Query('today', new ParseDatePipe({ optional: true, transform: true })) today: Date | undefined, //
- @Query('force') force: boolean | undefined,
- ) {
- await this.cronJobsService.generateRemessaVLT({ today, force });
- }
-
- @Get('generateRemessaEmpresa')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta a geração e envio de remessa - exatamente como no cronjob.' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'today', type: String, required: false, description: ApiDescription({ _: "Define uma data customizada para 'hoje'", example: '2024-07-15' }) })
- @ApiQuery({ name: 'force', type: Boolean, required: false, description: 'Ignora validação de data e afins no método do cronjob, e executa mesmo assim', example: false })
- async getGenerateRemessaConsorcio(
- @Query('today', new ParseDatePipe({ optional: true, transform: true })) today: Date | undefined, //
- @Query('force') force: boolean | undefined,
- ) {
- await this.cronJobsService.generateRemessaEmpresa({ today, force });
- }
- @Get('generateRemessaVanzeiros')
- @HttpCode(HttpStatus.OK)
- @UseGuards(AuthGuard('jwt'), RolesGuard)
- @Roles(RoleEnum.master)
- @ApiOperation({ description: 'Feito para manutenção pelos admins.\n\nExecuta a geração e envio de remessa - exatamente como no cronjob.' })
- @ApiBearerAuth()
- @ApiQuery({ name: 'today', type: String, required: false, description: ApiDescription({ _: "Define uma data customizada para 'hoje'", example: '2024-07-15' }) })
- @ApiQuery({ name: 'force', type: Boolean, required: false, description: 'Ignora validação de data e afins no método do cronjob, e executa mesmo assim', example: false })
- async getGenerateRemessaVanzeiros(
- @Query('today', new ParseDatePipe({ optional: true, transform: true })) today: Date | undefined, //
- @Query('force') force: boolean | undefined,
- ) {
- await this.cronJobsService.generateRemessaVanzeiros({ today, force });
- }
}
diff --git a/src/cron-jobs/cron-jobs.module.ts b/src/cron-jobs/cron-jobs.module.ts
index 906f7ce48..0e514ecf4 100644
--- a/src/cron-jobs/cron-jobs.module.ts
+++ b/src/cron-jobs/cron-jobs.module.ts
@@ -19,7 +19,7 @@ import { CronJobsManutencaoController } from './cron-jobs-manutencao.controller'
MailModule,
UsersModule,
MailCountModule,
- CnabModule,
+ CnabModule
],
controllers: [CronJobsManutencaoController],
providers: [CronJobsService],
diff --git a/src/cron-jobs/cron-jobs.service.ts b/src/cron-jobs/cron-jobs.service.ts
index 11431d5c4..224da9d39 100644
--- a/src/cron-jobs/cron-jobs.service.ts
+++ b/src/cron-jobs/cron-jobs.service.ts
@@ -1,10 +1,19 @@
-import { HttpStatus, Injectable, NotImplementedException } from '@nestjs/common';
+import { HttpStatus, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { SchedulerRegistry } from '@nestjs/schedule';
import { CronJob, CronJobParameters } from 'cron';
-import { addDays, endOfDay, isFriday, isMonday, isSaturday, isSunday, isThursday, isTuesday, isWithinInterval, setHours, startOfDay, subDays, subHours } from 'date-fns';
-import { CnabService, ICnabInfo } from 'src/cnab/cnab.service';
-import { PagadorContaEnum } from 'src/cnab/enums/pagamento/pagador.enum';
+import { HeaderName } from 'src/cnab/enums/pagamento/header-arquivo-status.enum';
+import { RemessaService } from 'src/cnab/novo-remessa/service/remessa.service';
+import { RetornoService } from 'src/cnab/novo-remessa/service/retorno.service';
+import {
+ isMonday,
+ isSaturday,
+ isSunday,
+ isTuesday,
+ subDays,
+} from 'date-fns';
+import { CnabService } from 'src/cnab/cnab.service';
+import { OrdemPagamentoService } from 'src/cnab/novo-remessa/service/ordem-pagamento.service';
import { InviteStatus } from 'src/mail-history-statuses/entities/mail-history-status.entity';
import { InviteStatusEnum } from 'src/mail-history-statuses/mail-history-status.enum';
import { MailHistory } from 'src/mail-history/entities/mail-history.entity';
@@ -18,8 +27,11 @@ import { SettingsService } from 'src/settings/settings.service';
import { User } from 'src/users/entities/user.entity';
import { UsersService } from 'src/users/users.service';
import { CustomLogger } from 'src/utils/custom-logger';
-import { formatDateInterval, formatDateISODate } from 'src/utils/date-utils';
import { validateEmail } from 'validations-br';
+import { OrdemPagamentoAgrupadoService } from '../cnab/novo-remessa/service/ordem-pagamento-agrupado.service';
+import { AllPagadorDict } from '../cnab/interfaces/pagamento/all-pagador-dict.interface';
+import { DistributedLockService } from '../cnab/novo-remessa/service/distributed-lock.service';
+import {nextFriday, nextThursday, previousFriday, isFriday, isThursday} from 'date-fns';
/**
* Enum CronJobServicesJobs
@@ -30,15 +42,11 @@ export enum CronJobsEnum {
pollDb = 'pollDb',
bulkResendInvites = 'bulkResendInvites',
updateRetorno = 'updateRetorno',
- updateTransacaoViewEmpresa = 'updateTransacaoViewEmpresa',
- updateTransacaoViewVan = 'updateTransacaoViewVan',
- updateTransacaoViewVLT = 'updateTransacaoViewVLT',
- updateTransacaoViewValues = 'updateTransacaoViewValues',
- syncTransacaoViewOrdem = 'syncTransacaoViewOrdem',
generateRemessaVLT = 'generateRemessaVLT',
generateRemessaEmpresa = 'generateRemessaEmpresa',
generateRemessaVanzeiros = 'generateRemessaVanzeiros',
generateRemessaLancamento = 'generateRemessaLancamento',
+ sincronizarEAgruparOrdensPagamento = 'sincronizarEAgruparOrdensPagamento'
}
interface ICronjobDebug {
/** Define uma data customizada para 'hoje' */
@@ -66,15 +74,24 @@ export class CronJobsService {
public jobsConfig: ICronJob[] = [];
+ private static readonly MODAIS = ['STPC', 'STPL', 'TEC'];
+ private static readonly CONSORCIOS = ['VLT', 'Intersul', 'Transcarioca', 'Internorte', 'MobiRio', 'Santa Cruz'];
+
constructor(
- private configService: ConfigService, //
+ private configService: ConfigService,
private settingsService: SettingsService,
private schedulerRegistry: SchedulerRegistry,
private mailService: MailService,
private mailHistoryService: MailHistoryService,
private usersService: UsersService,
private cnabService: CnabService,
- ) {}
+ private ordemPagamentoAgrupadoService: OrdemPagamentoAgrupadoService,
+ private remessaService: RemessaService,
+ private retornoService: RetornoService,
+ private ordemPagamentoService: OrdemPagamentoService,
+ private distributedLockService: DistributedLockService,
+ ) { }
+
onModuleInit() {
this.onModuleLoad().catch((error: Error) => {
@@ -82,9 +99,12 @@ export class CronJobsService {
});
}
- async onModuleLoad() {
- await this.updateTransacaoViewValues()
-
+ async onModuleLoad() {
+ //CHAMADAS PARA TESTE
+ // await this.remessaVLTExec();
+ // await this.remessaModalExec();
+ // await this.remessaConsorciosExec();
+
const THIS_CLASS_WITH_METHOD = 'CronJobsService.onModuleLoad';
this.jobsConfig.push(
{
@@ -109,110 +129,71 @@ export class CronJobsService {
cronJobParameters: {
cronTime: '*/30 * * * *', // Every 30 min
onTick: async () => {
- if (!this.validateJobsRemessa()) {
- this.logger.log(`Ignorando esta tarefa para não impedir tarefas prioritárias..`, 'saveRetornoPagamento');
- return;
- }
- await this.saveRetornoPagamento();
+ // await this.retornoExec();
},
},
},
{
/**
- * Atualizar Transações Vanzeiro - DLake para CCT para CCT - todo dia, a cada 30m
+ * Envio de Relatório Estatística dos Dados - todo dia, 06:00 - 06:01
*
- * Atualizar transações BQ Van
+ * NÃO DESABILITAR ENVIO DE REPORT - Every day, 09:00 GMT = 06:00 BRT (GMT-3)
+ *
+ * Envio relatório estatística
*/
- name: CronJobsEnum.updateTransacaoViewVan,
+ name: CronJobsEnum.sendReport,
cronJobParameters: {
- cronTime: '*/30 * * * *', // Every 30 min
- onTick: async () => {
- if (!this.validateJobsRemessa()) {
- this.logger.log(`Ignorando esta tarefa para não impedir tarefas prioritárias..`, 'saveRetornoPagamento');
- return;
- }
- await this.updateTransacaoViewBigquery('Van');
- },
+ cronTime: (await this.settingsService.getOneBySettingData(appSettings.any__mail_report_cronjob,
+ true, THIS_CLASS_WITH_METHOD)).getValueAsString(),
+ onTick: async () => await this.sendStatusReport(),
},
},
{
/**
- * Atualizar Transações Empresa - DLake para CCT - todo dia, 05:00, duração: 5 min
+ * Gerar arquivo remessa do Consórcio VLT - 2a-6a, 08:00, duração: 15 min
*
- * Atualizar transações BQ empresa
+ * Gerar remessa VLT
*/
- name: CronJobsEnum.updateTransacaoViewEmpresa,
+ name: CronJobsEnum.generateRemessaVLT,
cronJobParameters: {
- cronTime: '0 8 * * *', // Every day, 08:00 GMT = 05:00 BRT (GMT-3)
+ cronTime: '0 11 * * *', // Every day, 11:00 GMT = 08:00 BRT (GMT-3)
onTick: async () => {
- await this.updateTransacaoViewBigquery('Empresa');
+ const today = new Date();
+ if (isSaturday(today) || isSunday(today)) {
+ return;
+ }
+ await this.remessaVLTExec();
},
},
},
{
/**
- * Atualizar Transações VLT - DLake para CCT - todo dia, 05:10, duração: 5 min
+ * Gerar arquivo remessa dos vanzeiros - toda 6a, 10:00, duração: 15 min
*
- * Atualizar transações BQ VLT
+ * Gerar remessa vanzeiros
*/
- name: CronJobsEnum.updateTransacaoViewVLT,
+ name: CronJobsEnum.generateRemessaVanzeiros,
cronJobParameters: {
- cronTime: '10 8 * * *', // Every day, 08:10 GMT = 05:10 BRT (GMT-3)
+ cronTime: '0 16 * * THU', // Rodar todas as quintas 16:00 GMT = 13:00 BRT (GMT-3)
onTick: async () => {
- await this.updateTransacaoViewBigquery('VLT');
+ await this.remessaModalExec();
},
},
},
{
/**
- * Envio de Relatório Estatística dos Dados - todo dia, 06:00 - 06:01
- *
- * NÃO DESABILITAR ENVIO DE REPORT - Every day, 09:00 GMT = 06:00 BRT (GMT-3)
+ * Gerar arquivo Remessa dos Consórcios - toda 5a, 13:00, duração: 15 min
*
- * Envio relatório estatística
+ * Gerar remessa consórcios
*/
- name: CronJobsEnum.sendReport,
+ name: CronJobsEnum.generateRemessaEmpresa,
cronJobParameters: {
- cronTime: (await this.settingsService.getOneBySettingData(appSettings.any__mail_report_cronjob, true, THIS_CLASS_WITH_METHOD)).getValueAsString(),
- onTick: async () => await this.sendStatusReport(),
- },
- },
- {
- /**
- * Gerar arquivo remessa do Consórcio VLT - 2a-6a, 08:00, duração: 15 min
- * + BD do CCT - Sincronizar Transações com Ordem Pgto
- *
- * Gerar remessa VLT
- */
- name: CronJobsEnum.generateRemessaVLT,
- cronJobParameters: {
- cronTime: '0 11 * * *', // Every day, 11:00 GMT = 08:00 BRT (GMT-3)
+ cronTime: '0 13 * * THU', // Rodar todas as quintas 13:00 GMT = 10:00 BRT (GMT-3)
onTick: async () => {
- const today = new Date();
- if (isSaturday(today) || isSunday(today)) {
- return;
- }
- await this.generateRemessaVLT();
- await this.syncTransacaoViewOrdem('vlt');
+ await this.remessaConsorciosExec();
},
},
},
- // {
- // /**
- // * Gerar arquivo remessa dos vanzeiros - toda 6a, 10:00, duração: 15 min
- // * + BD do CCT - Sincronizar Transações com Ordem Pgto
- // *
- // * Gerar remessa vanzeiros
- // */
- // name: CronJobsEnum.generateRemessaVanzeiros,
- // cronJobParameters: {
- // cronTime: '0 13 * * 5', // Every Friday (see method), 13:00 GMT = 10:00 BRT (GMT-3)
- // onTick: async () => {
- // await this.generateRemessaVanzeiros();
- // await this.syncTransacaoViewOrdem('van');
- // },
- // },
- // },
{
/**
* Reenvio de E-mail para Vanzeiros - 1 aceso ou Cadastro de Contas Bancárias - dia 15 de cada mês, 11:45, duração: 5 min
@@ -221,38 +202,12 @@ export class CronJobsService {
*/
name: CronJobsEnum.bulkResendInvites,
cronJobParameters: {
- cronTime: '45 14 15 * *', // Day 15, 14:45 GMT = 11:45 BRT (GMT-3)
- onTick: async () => await this.bulkResendInvites(),
- },
- },
- {
- /**
- * Atualizar Transações Campos Nulos CCT - DLake para CCT - todo dia, 12:00, duração: 10 min
- *
- * Atualizar transações com campos nulos do BQ
- */
- name: CronJobsEnum.updateTransacaoViewValues,
- cronJobParameters: {
- cronTime: '0 15 * * *', // Every day, 15:00 GMT = 12:00 BRT (GMT-3)
- onTick: async () => await this.updateTransacaoViewValues(),
+ cronTime: '0 16 * * THU', // Rodar todas as quintas 16:00 GMT = 13:00 BRT (GMT-3)
+ onTick: async () => {
+ await this.remessaModalExec();
+ },
},
},
- // {
- // /**
- // * Gerar arquivo Remessa dos Consórcios - toda 5a, 17:00, duração: 15 min
- // * + BD do CCT - Sincronizar Transações com Ordem Pgto
- // *
- // * Gerar remessa consórcios
- // */
- // name: CronJobsEnum.generateRemessaEmpresa,
- // cronJobParameters: {
- // cronTime: '0 20 * * *', // Every Thursday (see method), 20:00 GMT = 17:00 BRT (GMT-3)
- // onTick: async () => {
- // await this.generateRemessaEmpresa();
- // await this.syncTransacaoViewOrdem('empresa');
- // },
- // },
- // },
{
/**
* Envio do E-mail - Convite para o usuário realizar o 1o acesso no Sistema CCT - todo dia, 19:00, duração: 5 min
@@ -265,18 +220,16 @@ export class CronJobsService {
onTick: async () => await this.bulkSendInvites(),
},
},
- // {
- // /**
- // * Gerar arquivo Remessa do Lançamento - todo dia, duração: 15 min
- // */
- // name: CronJobsEnum.generateRemessaLancamento,
- // cronJobParameters: {
- // cronTime: '0 17 * * *', // Every day, 17:00 GMT = 14:00 BRT (GMT-3)
- // onTick: async () => {
- // await this.generateRemessaLancamento();
- // },
- // },
- // },
+ {
+ /**
+ * Sincroniza e agrupa ordens de pagamento.
+ * */
+ name: CronJobsEnum.sincronizarEAgruparOrdensPagamento,
+ cronJobParameters: {
+ cronTime: "0 9-21 * * *", // 06:00 BRT (GMT-3) = 09:00 GMT, 18:00 BRT (GMT-3) = 21:00 GMT
+ onTick: async () => await this.sincronizarEAgruparOrdensPagamento(),
+ },
+ },
);
/** NÃO COMENTE ISTO, É A GERAÇÃO DE JOBS */
@@ -296,7 +249,7 @@ export class CronJobsService {
* - É staging se .env -> nodeEnv = production, Banco -> settings.api_env = staging
*/
public async getIsProd(method?: string) {
- const apiEnv = await this.settingsService.getOneBySettingData(appSettings.any__api_env);
+ const apiEnv = await this.settingsService.getOneBySettingData(appSettings.any__bigquery_env);
const nodeEnv = this.configService.getOrThrow('app.nodeEnv', { infer: true });
const isProd = nodeEnv === 'production' && apiEnv.getValueAsString() === 'production';
if (method !== undefined && !isProd) {
@@ -322,228 +275,11 @@ export class CronJobsService {
job.start();
}
- /**
- * Gera na quinta, paga na sexta.
- */
- //TODO: GERAR PAGAMENTO SEXTA
- async generateRemessaEmpresa(debug?: ICronjobDebug) {
- const METHOD = 'generateRemessaEmpresa';
- try {
- const today = debug?.today || new Date();
- if (!isThursday(today)) {
- this.logger.log('Não implementado - Hoje não é quinta-feira. Abortando...', METHOD);
- return;
- }
- if (!(await this.getIsCnabJobEnabled(METHOD)) && !debug?.force) {
- return;
- }
- if (!isThursday(today)) {
- this.logger.error('Não implementado - Hoje não é quinta-feira. Abortando...', undefined, METHOD);
- return;
- }
- this.logger.log('Tarefa iniciada', METHOD);
- const startDate = new Date();
- const sex = subDays(today, 6);
- const qui = today;
- await this.cnabService.saveTransacoesJae(sex, qui, 0, 'Empresa');
- const listCnab = await this.cnabService.generateRemessa({
- tipo: PagadorContaEnum.ContaBilhetagem,
- dataPgto: addDays(today, 1),
- isConference: false,
- isCancelamento: false,
- isTeste: false,
- });
- await this.cnabService.sendRemessa(listCnab);
- this.logger.log(`Tarefa finalizada - ${formatDateInterval(new Date(), startDate)}`, METHOD);
- } catch (error) {
- this.logger.error('Erro ao executar tarefa.', error?.stack, METHOD);
- }
- }
-
- /**
- * Gera e envia remessa da semana atual, a ser pago numa sexta-feira.
- */
- async generateRemessaVanzeiros(debug?: ICronjobDebug,isUnico?:boolean) {
- const METHOD = 'generateRemessaVanzeiros';
- // if (!this.validateGenerateRemessaVanzeiros(METHOD, debug)) {
- // return;
- // }
- this.logger.log('Tarefa iniciada', METHOD);
- const startDate = new Date();
- const today = debug?.today || new Date();
- const sex = subDays(today, 7);
- const qui = subDays(today, 1);
-
- await this.cnabService.saveTransacoesJae(sex, qui, 0,'Van');
- const listCnab = await this.cnabService.generateRemessa({ tipo: PagadorContaEnum.ContaBilhetagem
- , dataPgto: today, isConference: false, isCancelamento: false, isTeste: false });
- await this.cnabService.sendRemessa(listCnab);
- this.logger.log(`Tarefa finalizada - ${formatDateInterval(new Date(), startDate)}`, METHOD);
- }
-
- private validateGenerateRemessaVanzeiros(method: string, debug?: ICronjobDebug): boolean {
- const today = debug?.today || new Date();
- if (!isFriday(today)) {
- this.logger.error('Não implementado - Hoje não é sexta-feira. Abortando...', undefined, method);
- return false;
- }
- return true;
- }
-
- /**
- * Regras de negócio:
- * - Se hoje for terça, obter de sáb, dom, seg
- * - Se hoje for segunda, obter de sexta apenas
- * - Se hoje for demais dias, obter 1 dia anterior
- */
- public async generateRemessaVLT(debug?: ICronjobDebug) {
- const METHOD = 'generateRemessaVLT';
- this.logger.log('Tarefa iniciada', METHOD);
- const today = debug?.today || new Date();
- const startDateLog = new Date();
- /** defaut: qua,qui,sex,sáb,dom */
- let daysBeforeBegin = 1;
- let daysBeforeEnd = 1;
- if (isMonday(today)) {
- daysBeforeBegin = 3;
- daysBeforeEnd = 3;
- } else if (isTuesday(today)) {
- daysBeforeBegin = 3;
- }
- const startDate = subDays(today, daysBeforeBegin);
- const endDate = subDays(today, daysBeforeEnd);
- await this.cnabService.saveTransacoesJae(startDate, endDate, undefined, 'VLT');
- const listCnab = await this.cnabService.generateRemessa({
- tipo: PagadorContaEnum.ContaBilhetagem,
- dataPgto: new Date(),
- isConference: false,
- isCancelamento: false,
- isTeste: false,
- });
- await this.cnabService.sendRemessa(listCnab);
- this.logger.log(`Tarefa finalizada - ${formatDateInterval(new Date(), startDateLog)}`, METHOD);
- }
-
- /**
- * Regras de negócio:
- * - Todo dia gera remessa de Lançamentos com dataOrdem = hoje
- */
- public async generateRemessaLancamento(debug?: ICronjobDebug) {
- const METHOD = 'generateRemessaLancamento';
- try {
- this.logger.log('Tarefa iniciada', METHOD);
- const today = debug?.today || new Date();
- const startDateLog = new Date();
- const startDate = today;
- const endDate = today;
- throw new NotImplementedException();
- // await this.cnabService.saveTransacoesLancamento(startDate, endDate);
- const listCnab = await this.cnabService.generateRemessa({
- tipo: PagadorContaEnum.CETT,
- dataPgto: undefined, // data programada no Lançamento
- isConference: false,
- isCancelamento: false,
- isTeste: false,
- });
- await this.cnabService.sendRemessa(listCnab);
- this.logger.log(`Tarefa finalizada - ${formatDateInterval(new Date(), startDateLog)}`, METHOD);
- } catch (error) {
- this.logger.error('Erro ao executar tarefa.', error?.stack, METHOD);
- }
- }
-
- public async syncTransacaoViewOrdem(filterConsorcios?: 'vlt' | 'van' | 'empresa') {
- const METHOD = `syncTransacaoViewOrdem`;
- try {
- const startDate = subDays(new Date(), 30);
- const today = new Date();
- this.logger.log(`Sincronizando TransacaoViews ${filterConsorcios || ''} entre ${formatDateISODate(startDate)} e ${formatDateISODate(today)}`, METHOD);
- const consorcio: { in: string[]; notIn: string[] } = { in: [], notIn: [] };
- if (filterConsorcios === 'van') {
- consorcio.in.push('STPC');
- consorcio.in.push('STPL');
- consorcio.in.push('TEC');
- } else if (filterConsorcios === 'vlt') {
- consorcio.in.push('VLT');
- } else if (filterConsorcios === 'empresa') {
- consorcio.notIn = ['STPC', 'STPL', 'TEC', 'VLT'];
- }
- await this.cnabService.syncTransacaoViewOrdemPgto({ consorcio, dataOrdem_between: [startOfDay(startDate), endOfDay(today)] });
- this.logger.log(`Trefa finalizada com sucesso.`, METHOD);
- } catch (error) {
- this.logger.error('Erro ao executar tarefa.', error?.stack, METHOD);
- }
- }
-
- public async saveAndSendRemessa(dataPgto: Date, isConference = false, isCancelamento = false, nsaInicial = 0, nsaFinal = 0, dataCancelamento = new Date()) {
- const listCnabStr = await this.cnabService.generateRemessa({
- tipo: PagadorContaEnum.ContaBilhetagem,
- dataPgto,
- isConference,
- isCancelamento,
- isTeste: false,
- nsaInicial,
- nsaFinal,
- dataCancelamento,
- });
- if (listCnabStr) await this.sendRemessa(listCnabStr);
- }
deleteCron(jobName: string) {
this.schedulerRegistry.deleteCronJob(jobName);
}
- /**
- * Atualiza todos os itens do dia de ontem.
- *
- * @param consorcio
- * `Van`: De 30 em 30 minutos, buscar até 8 dias atrás.
- *
- * `VLT`: Todo dia pega 1 dia antes.
- *
- * `Empresa`: Todo dia pega no dia atual.
- *
- */
- async updateTransacaoViewBigquery(consorcio: 'Van' | 'Empresa' | 'VLT', debug?: ICronjobDebug) {
- const METHOD = this.updateTransacaoViewBigquery.name;
- try {
- if (!(await this.getIsCnabJobEnabled(METHOD)) && !debug?.force) {
- return;
- }
- const today = debug?.today || new Date();
- let startDate = today;
- let endDate = today;
-
- try {
- this.logger.log('Iniciando tarefa.', METHOD);
- if (consorcio == 'Van') {
- startDate = subDays(startDate, 8);
- } else if (consorcio == 'VLT') {
- startDate = subDays(startDate, 1);
- } else {
- /** Empresa */
- startDate = startOfDay(startDate);
- endDate = endOfDay(endDate);
- }
- await this.cnabService.updateTransacaoViewBigquery(startDate, endDate, 0, consorcio);
- this.logger.log('TransacaoViews atualizados com sucesso.', METHOD);
- } catch (error) {
- this.logger.error(`ERRO CRÍTICO - ${JSON.stringify(error)}`, error?.stack, METHOD);
- }
- } catch (error) {
- this.logger.error('Erro ao executar tarefa.', error?.stack, METHOD);
- }
- }
-
- async updateTransacaoViewValues() {
- const METHOD = this.updateTransacaoViewValues.name;
- try {
- await this.cnabService.updateTransacaoViewBigqueryValues(8);
- } catch (error) {
- this.logger.error('Erro ao executar tarefa.', error?.stack, METHOD);
- }
- }
-
async bulkSendInvites() {
const METHOD = this.bulkSendInvites.name;
try {
@@ -884,76 +620,120 @@ export class CronJobsService {
}
}
- async sendRemessa(listCnab: ICnabInfo[]) {
- const METHOD = this.sendRemessa.name;
+ async readRetornoExtrato() {
+ const METHOD = 'readRetornoExtrato';
try {
- this.logger.log('Iniciando tarefa.', METHOD);
- await this.cnabService.sendRemessa(listCnab);
+ await this.cnabService.readRetornoExtrato();
this.logger.log('Tarefa finalizada com sucesso.', METHOD);
} catch (error) {
this.logger.error(`Erro ao executar tarefa, abortando. - ${error}`, error?.stack, METHOD);
}
}
- async saveRetornoPagamento() {
- const METHOD = this.saveRetornoPagamento.name;
- try {
- await this.cnabService.readRetornoPagamento();
- this.logger.log('Tarefa finalizada com sucesso.', METHOD);
- } catch (error) {
- this.logger.error(`Erro ao executar tarefa, abortando. - ${error}`, error?.stack, METHOD);
+ private async geradorRemessaExec(dataInicio: Date, dataFim: Date, dataPagamento: Date,
+ consorcios: string[], headerName: HeaderName) {
+ //Agrupa pagamentos
+
+ for (let index = 0; index < consorcios.length; index++) {
+ await this.ordemPagamentoAgrupadoService.prepararPagamentoAgrupados(dataInicio,
+ dataFim, dataPagamento, "contaBilhetagem", [consorcios[index]]);
}
+ //Prepara o remessa
+ await this.remessaService.prepararRemessa(dataInicio, dataFim, consorcios);
+ //Gera o TXT
+ const txt = await this.remessaService.gerarCnabText(headerName);
+ //Envia para o SFTP
+ await this.remessaService.enviarRemessa(txt);
}
- validateJobsRemessa() {
+ async remessaVLTExec() {
+ //Rodar de segunda a sexta
const today = new Date();
- const isValid = !this.isDateInRemessaVan(today) && !this.isDateInRemessaVLT(today) && !this.isDateInRemessaConsorcio(today);
- return isValid;
+ /** defaut: qua,qui,sex,sáb,dom */
+ let daysBeforeBegin = 1;
+ let daysBeforeEnd = 1;
+ if (isMonday(today)) {
+ daysBeforeBegin = 3;
+ daysBeforeEnd = 3;
+ } else if (isTuesday(today)) {
+ daysBeforeBegin = 3;
+ }
+ const dataInicio = subDays(today, daysBeforeBegin);
+ const dataFim = subDays(today, daysBeforeEnd);
+ await this.geradorRemessaExec(dataInicio, dataFim, today,
+ ['VLT'], HeaderName.VLT);
}
- /**
- * Requisitos:
- * O job do remessa Van roda toda sexta, 10:00 - 10:30, usaremos uma gordura de 10m
- *
- * Ou seja: 09:50 - 10:40 BRT (GMT-3) ou 12:50 - 13:40 GMT (código)
- */
- isDateInRemessaVan(date: Date): boolean {
- const dateStr = formatDateISODate(date);
- const hourInterval = { start: new Date(`${dateStr} 12:50`), end: new Date(`${dateStr} 13:40`) };
- return isFriday(date) && isWithinInterval(date, hourInterval);
+ async remessaModalExec() {
+ //Rodar Quinta
+ const today = new Date();
+ const dataInicio = subDays(today, 6);
+ const dataFim = subDays(today, 0);
+ await this.geradorRemessaExec(dataInicio, dataFim, today /*addDays(today,1)*/,
+ ['STPC', 'STPL', 'TEC'], HeaderName.MODAL);
}
- /**
- * Requisitos:
- * O job do remessa VLT roda todo dia, 08:00 - 08:30, usaremos uma gordura de 10m
- *
- * Ou seja: 07:50 - 08:40 BRT (GMT-3) ou 10:50 - 11:40 GMT
- */
- isDateInRemessaVLT(date: Date): boolean {
- const dateStr = formatDateISODate(date);
- const hourInterval = { start: new Date(`${dateStr} 10:50`), end: new Date(`${dateStr} 11:40`) };
- return isWithinInterval(date, hourInterval);
+ async remessaConsorciosExec() {
+ //Rodar na Quinta
+ const today = new Date();
+ const dataInicio = subDays(today, 6);
+ const dataFim = subDays(today, 0);
+ await this.geradorRemessaExec(dataInicio, dataFim, today /*addDays(today,1)*/,
+ ['Internorte', 'Intersul', 'MobiRio', 'Santa Cruz', 'Transcarioca'], HeaderName.CONSORCIO);
}
- /**
- * Requisitos:
- * O job do remessa consórcio roda toda quinta, 17:00 - 17:30, usaremos uma gordura de 10m
- *
- * Ou seja: 16:50 - 17:40 BRT (GMT-3) ou 19:50 - 20:40 GMT (código)
- */
- isDateInRemessaConsorcio(date: Date): boolean {
- const dateStr = formatDateISODate(date);
- const hourInterval = { start: new Date(`${dateStr} 19:50`), end: new Date(`${dateStr} 20:40`) };
- return isThursday(date) && isWithinInterval(date, hourInterval);
+ async retornoExec() {
+ const txt = await this.retornoService.lerRetornoSftp();
+ if (txt)
+ await this.retornoService.salvarRetorno({ name: txt?.name, content: txt?.content });
}
- async readRetornoExtrato() {
- const METHOD = 'readRetornoExtrato';
- try {
- await this.cnabService.readRetornoExtrato();
- this.logger.log('Tarefa finalizada com sucesso.', METHOD);
- } catch (error) {
- this.logger.error(`Erro ao executar tarefa, abortando. - ${error}`, error?.stack, METHOD);
+
+ async sincronizarEAgruparOrdensPagamento() {
+ const METHOD = 'sincronizarEAgruparOrdensPagamento';
+ this.logger.log('Tentando adquirir lock para execução da tarefa de sincronização e agrupamento.');
+ const locked = await this.distributedLockService.acquireLock(METHOD);
+ if (locked) {
+ try {
+ this.logger.log('Lock adquirido para a tarefa de sincronização e agrupamento.');
+ // Sincroniza as ordens de pagamento para todos os modais e consorcios
+ const nextThursday = this.getNextThursday();
+ const lastFriday = this.getLastFriday();
+ const nextFriday = this.getNextFriday();
+ this.logger.log(`Iniciando sincronização das ordens de pagamento do BigQuery. Data de Início: ${lastFriday.toISOString()}, Data Fim: ${nextThursday.toISOString()}`, METHOD);
+ const consorciosEModais = [...CronJobsService.CONSORCIOS, ...CronJobsService.MODAIS];
+ await this.ordemPagamentoService.sincronizarOrdensPagamento(lastFriday, nextThursday, consorciosEModais);
+ this.logger.log('Sincronização finalizada. Iniciando agrupamento para modais.', METHOD);
+ const pagadorKey: keyof AllPagadorDict = 'contaBilhetagem';
+ // Agrupa para os modais
+ await this.ordemPagamentoAgrupadoService.prepararPagamentoAgrupados(lastFriday, nextThursday, nextFriday, pagadorKey, CronJobsService.MODAIS);
+ this.logger.log('Tarefa finalizada com sucesso.', METHOD);
+ } catch (error) {
+ this.logger.error(`Erro ao executar tarefa, abortando. - ${error}`, error?.stack, METHOD);
+ } finally {
+ await this.distributedLockService.releaseLock(METHOD);
+ }
+ } else {
+ this.logger.log('Não foi possível adquirir o lock para a tarefa de sincronização e agrupamento.');
+ }
+ }
+
+ getNextThursday(date = new Date()) {
+ if (isThursday(date)) {
+ return new Date(date.toISOString().split('T')[0]);
}
+ return nextThursday(date);
}
+
+ getLastFriday(date = new Date()) {
+ if (isFriday(date)) {
+ return new Date(date.toISOString().split('T')[0]);
+ }
+ return previousFriday(date);
+ }
+
+ getNextFriday(date = new Date()) {
+ return nextFriday(date);
+ }
+
}
diff --git a/src/database/migrations/1732654821584-UpdatePgtoIndevido.ts b/src/database/migrations/1732654821584-UpdatePgtoIndevido.ts
new file mode 100644
index 000000000..4e6ba5b62
--- /dev/null
+++ b/src/database/migrations/1732654821584-UpdatePgtoIndevido.ts
@@ -0,0 +1,32 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class UpdatePgtoIndevido1732654821584 implements MigrationInterface {
+ name = 'UpdatePgtoIndevido1732654821584';
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "transacao_agrupado" DROP COLUMN IF EXISTS "isUnico"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" DROP COLUMN "dataPagamento"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ADD "dataPagamento" TIMESTAMP`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" DROP COLUMN "dataReferencia"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ADD "dataReferencia" TIMESTAMP`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPago" TYPE numeric(10,5)`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPago" DROP NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPagar" TYPE numeric(10,5)`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPagar" DROP NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "saldoDevedor" TYPE numeric(10,5)`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "saldoDevedor" DROP NOT NULL`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "saldoDevedor" SET NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "saldoDevedor" TYPE numeric`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPagar" SET NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPagar" TYPE numeric`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPago" SET NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPago" TYPE numeric`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" DROP COLUMN "dataReferencia"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ADD "dataReferencia" date`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" DROP COLUMN "dataPagamento"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ADD "dataPagamento" date NOT NULL`);
+ }
+}
diff --git a/src/database/migrations/1733320803515-NovoRemessa.ts b/src/database/migrations/1733320803515-NovoRemessa.ts
new file mode 100644
index 000000000..2b0ccd9ee
--- /dev/null
+++ b/src/database/migrations/1733320803515-NovoRemessa.ts
@@ -0,0 +1,18 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class NovoRemessa1733320803515 implements MigrationInterface {
+ name = 'NovoRemessa1733320803515'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE "ordem_pagamento" ("id" integer NOT NULL, "dataOrdem" date NOT NULL, "nomeConsorcio" character varying(200), "nomeOperadora" character varying(200), "userId" integer NOT NULL, "valor" numeric(13,5) NOT NULL, "idOrdemPagamento" character varying, "idOperadora" character varying, "operadoraCpfCnpj" character varying, "idConsorcio" character varying, "consorcioCnpj" character varying, "bqUpdatedAt" TIMESTAMP NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "ordemPgamentoAgrupadoId" integer, CONSTRAINT "PK_OrdemPagamentoId" PRIMARY KEY ("id"))`);
+ await queryRunner.query(`CREATE TABLE "ordem_pagamento_agrupado" ("id" SERIAL NOT NULL, "userId" character varying NOT NULL, "userBankCode" character varying NOT NULL, "userBankAgency" character varying NOT NULL, "userBankAccount" character varying NOT NULL, "userBankAccountDigit" character varying NOT NULL, "dataPagamento" TIMESTAMP NOT NULL DEFAULT now(), "valorTotal" numeric(13,5) NOT NULL, "statusRemessa" character varying NOT NULL, "isPago" boolean NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_OrdemPagamentoAgrupadoId" PRIMARY KEY ("id"))`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" ADD CONSTRAINT "FK_OrdemPagamentoAgrupado_ManyToOne" FOREIGN KEY ("ordemPgamentoAgrupadoId") REFERENCES "ordem_pagamento_agrupado"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" DROP CONSTRAINT "FK_OrdemPagamentoAgrupado_ManyToOne"`);
+ await queryRunner.query(`DROP TABLE "ordem_pagamento_agrupado"`);
+ await queryRunner.query(`DROP TABLE "ordem_pagamento"`);
+ }
+
+}
diff --git a/src/database/migrations/1734714076564-OrdemPagamentoDataCaptura.ts b/src/database/migrations/1734714076564-OrdemPagamentoDataCaptura.ts
new file mode 100644
index 000000000..510f4e439
--- /dev/null
+++ b/src/database/migrations/1734714076564-OrdemPagamentoDataCaptura.ts
@@ -0,0 +1,44 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class OrdemPagamentoDataCaptura1734714076564 implements MigrationInterface {
+ name = 'OrdemPagamentoDataCaptura1734714076564'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE "ordem_pagamento_agrupado_historico" ("id" SERIAL NOT NULL, "dataReferencia" TIMESTAMP NOT NULL, "userBankCode" character varying NOT NULL, "userBankAgency" character varying NOT NULL, "userBankAccount" character varying NOT NULL, "userBankAccountDigit" character varying NOT NULL, "statusRemessa" integer NOT NULL, "motivoStatusRemessa" character varying, "ordemPagamentoAgrupadoId" integer, CONSTRAINT "PK_OrdemPagamentoAgrupadoHistoricoId" PRIMARY KEY ("id"))`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" DROP COLUMN "ordemPgamentoAgrupadoId"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" DROP COLUMN "consorcioCnpj"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP COLUMN "userBankAccount"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP COLUMN "userId"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP COLUMN "isPago"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP COLUMN "userBankAgency"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP COLUMN "userBankCode"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP COLUMN "statusRemessa"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP COLUMN "userBankAccountDigit"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" ADD "dataCaptura" TIMESTAMP`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" ADD "ordemPagamentoAgrupadoId" integer`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD "pagadorId" integer`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ALTER COLUMN "dataPagamento" DROP DEFAULT`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD CONSTRAINT "FK_OrdemPagamentoAgrupado_pagador_ManyToOne" FOREIGN KEY ("pagadorId") REFERENCES "pagador"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado_historico" ADD CONSTRAINT "FK_OrdemPagamentoAgrupadoHistorico_ordensPagamento_OneToMany" FOREIGN KEY ("ordemPagamentoAgrupadoId") REFERENCES "ordem_pagamento_agrupado_historico"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado_historico" DROP CONSTRAINT "FK_OrdemPagamentoAgrupadoHistorico_ordensPagamento_OneToMany"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP CONSTRAINT "FK_OrdemPagamentoAgrupado_pagador_ManyToOne"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ALTER COLUMN "dataPagamento" SET DEFAULT now()`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" DROP COLUMN "pagadorId"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" DROP COLUMN "ordemPagamentoAgrupadoId"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" DROP COLUMN "dataCaptura"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD "userBankAccountDigit" character varying NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD "statusRemessa" character varying NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD "userBankCode" character varying NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD "userBankAgency" character varying NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD "isPago" boolean NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD "userId" character varying NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado" ADD "userBankAccount" character varying NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" ADD "consorcioCnpj" character varying`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" ADD "ordemPgamentoAgrupadoId" integer`);
+ await queryRunner.query(`DROP TABLE "ordem_pagamento_agrupado_historico"`);
+ }
+
+}
diff --git a/src/database/migrations/1734726393654-OrdemPagamentoUserIdNullable.ts b/src/database/migrations/1734726393654-OrdemPagamentoUserIdNullable.ts
new file mode 100644
index 000000000..b1235fb51
--- /dev/null
+++ b/src/database/migrations/1734726393654-OrdemPagamentoUserIdNullable.ts
@@ -0,0 +1,16 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class OrdemPagamentoUserIdNullable1734726393654 implements MigrationInterface {
+ name = 'OrdemPagamentoUserIdNullable1734726393654'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" ALTER COLUMN "userId" DROP NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" ADD CONSTRAINT "FK_OrdemPagamentoAgrupado_ManyToOne" FOREIGN KEY ("ordemPagamentoAgrupadoId") REFERENCES "ordem_pagamento_agrupado"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" DROP CONSTRAINT "FK_OrdemPagamentoAgrupado_ManyToOne"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento" ALTER COLUMN "userId" SET NOT NULL`);
+ }
+
+}
diff --git a/src/database/migrations/1734982901641-FixOrdemPagamentoAgrupadoFK.ts b/src/database/migrations/1734982901641-FixOrdemPagamentoAgrupadoFK.ts
new file mode 100644
index 000000000..20c022f60
--- /dev/null
+++ b/src/database/migrations/1734982901641-FixOrdemPagamentoAgrupadoFK.ts
@@ -0,0 +1,16 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class FixOrdemPagamentoAgrupadoFK1734982901641 implements MigrationInterface {
+ name = 'FixOrdemPagamentoAgrupadoFK1734982901641'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado_historico" DROP CONSTRAINT "FK_OrdemPagamentoAgrupadoHistorico_ordensPagamento_OneToMany"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado_historico" ADD CONSTRAINT "FK_OrdemPagamentoAgrupadoHistorico_ordensPagamento_OneToMany" FOREIGN KEY ("ordemPagamentoAgrupadoId") REFERENCES "ordem_pagamento_agrupado"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado_historico" DROP CONSTRAINT "FK_OrdemPagamentoAgrupadoHistorico_ordensPagamento_OneToMany"`);
+ await queryRunner.query(`ALTER TABLE "ordem_pagamento_agrupado_historico" ADD CONSTRAINT "FK_OrdemPagamentoAgrupadoHistorico_ordensPagamento_OneToMany" FOREIGN KEY ("ordemPagamentoAgrupadoId") REFERENCES "ordem_pagamento_agrupado_historico"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ }
+
+}
diff --git a/src/database/migrations/1735929476563-CreateAgruparOrdensProcedure.ts b/src/database/migrations/1735929476563-CreateAgruparOrdensProcedure.ts
new file mode 100644
index 000000000..268365441
--- /dev/null
+++ b/src/database/migrations/1735929476563-CreateAgruparOrdensProcedure.ts
@@ -0,0 +1,79 @@
+import { MigrationInterface, QueryRunner } from "typeorm"
+
+export class CreateAgruparOrdensProcedure1735929476563 implements MigrationInterface {
+
+ public async up(queryRunner: QueryRunner): Promise {
+
+ await queryRunner.query(`
+ CREATE OR REPLACE PROCEDURE P_AGRUPAR_ORDENS(IN dataInicial DATE,
+ IN dataFinal DATE,
+ IN dataPagamento DATE,
+ IN pagadorId INT)
+ LANGUAGE plpgsql
+ AS $$
+ DECLARE
+ ordem RECORD;
+ ordemPagamentoAgrupadoId BIGINT;
+ BEGIN
+ -- Loop para percorrer cada ordem de pagamento com userId nao nulo
+ FOR ordem IN (
+ SELECT id, valor, "userId"
+ FROM public.ordem_pagamento
+ WHERE "userId" IS NOT NULL
+ AND "dataCaptura" BETWEEN dataInicial AND dataFinal
+ AND "ordemPagamentoAgrupadoId" IS NULL
+ )
+ LOOP
+ SELECT opa.id
+ INTO ordemPagamentoAgrupadoId
+ FROM public.ordem_pagamento_agrupado opa
+ INNER JOIN public.ordem_pagamento op
+ on opa.id = op."ordemPagamentoAgrupadoId"
+ WHERE "dataPagamento" = dataPagamento
+ AND "userId" = ordem."userId"
+ LIMIT 1;
+
+ IF ordemPagamentoAgrupadoId IS NULL THEN
+ INSERT INTO public.ordem_pagamento_agrupado (id, "dataPagamento", "valorTotal", "createdAt", "updatedAt", "pagadorId")
+ VALUES (nextval('ordem_pagamento_agrupado_id_seq'), dataPagamento, ordem.valor, current_timestamp, current_timestamp, pagadorId)
+ RETURNING id INTO ordemPagamentoAgrupadoId;
+
+ UPDATE public.ordem_pagamento
+ SET "ordemPagamentoAgrupadoId" = ordemPagamentoAgrupadoId
+ WHERE id = ordem.id;
+
+ INSERT INTO public.ordem_pagamento_agrupado_historico (id, "ordemPagamentoAgrupadoId", "dataReferencia", "userBankAccountDigit",
+ "userBankAccount", "userBankAgency", "userBankCode", "statusRemessa")
+ SELECT nextval('ordem_pagamento_agrupado_historico_id_seq'), ordemPagamentoAgrupadoId, current_timestamp, u."bankAccountDigit",
+ u."bankAccount", u."bankAgency", u."bankCode", 1
+ FROM public."user" u
+ inner join ordem_pagamento op
+ on u.id = op."userId"
+ inner join ordem_pagamento_agrupado opa
+ on op."ordemPagamentoAgrupadoId" = opa.id
+ WHERE opa.id = ordemPagamentoAgrupadoId;
+
+ ELSE
+ UPDATE public.ordem_pagamento_agrupado
+ SET "valorTotal" = "valorTotal" + ordem.valor,
+ "updatedAt" = current_timestamp
+ WHERE id = ordemPagamentoAgrupadoId;
+
+ UPDATE public.ordem_pagamento
+ SET "ordemPagamentoAgrupadoId" = ordemPagamentoAgrupadoId
+ WHERE id = ordem.id;
+ END IF;
+
+ END LOOP;
+ COMMIT;
+ END $$;
+ `);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`
+ DROP PROCEDURE IF EXISTS P_AGRUPAR_ORDENS;
+ `);
+ }
+
+}
diff --git a/src/database/migrations/1736271782542-HistoricoAlterTable.ts b/src/database/migrations/1736271782542-HistoricoAlterTable.ts
new file mode 100644
index 000000000..0a6c2f5ba
--- /dev/null
+++ b/src/database/migrations/1736271782542-HistoricoAlterTable.ts
@@ -0,0 +1,46 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class HistoricoAlterTable1736271782542 implements MigrationInterface {
+ name = 'HistoricoAlterTable1736271782542'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "ordem_pagamento" ("id" integer NOT NULL, "dataOrdem" date NOT NULL, "nomeConsorcio" character varying(200), "nomeOperadora" character varying(200), "userId" integer, "valor" numeric(13,5) NOT NULL, "idOrdemPagamento" character varying, "idOperadora" character varying, "operadoraCpfCnpj" character varying, "idConsorcio" character varying, "dataCaptura" TIMESTAMP, "bqUpdatedAt" TIMESTAMP NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "ordemPagamentoAgrupadoId" integer, CONSTRAINT "PK_OrdemPagamentoId" PRIMARY KEY ("id"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "ordem_pagamento_agrupado" ("id" SERIAL NOT NULL, "valorTotal" numeric(13,5) NOT NULL, "dataPagamento" TIMESTAMP NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "pagadorId" integer, CONSTRAINT "PK_OrdemPagamentoAgrupadoId" PRIMARY KEY ("id"))`);
+ await queryRunner.query(`CREATE TABLE IF NOT EXISTS "ordem_pagamento_agrupado_historico" ("id" SERIAL NOT NULL, "dataReferencia" TIMESTAMP NOT NULL, "userBankCode" character varying NOT NULL, "userBankAgency" character varying NOT NULL, "userBankAccount" character varying NOT NULL, "userBankAccountDigit" character varying NOT NULL, "statusRemessa" integer NOT NULL, "motivoStatusRemessa" character varying, "ordemPagamentoAgrupadoId" integer, CONSTRAINT "PK_OrdemPagamentoAgrupadoHistoricoId" PRIMARY KEY ("id"))`);
+ await queryRunner.query(`ALTER TABLE "transacao_agrupado" DROP COLUMN IF EXISTS "isUnico"`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" ADD "ordemPagamentoAgrupadoHistoricoId" integer`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" ADD CONSTRAINT "UQ_4acdbfdbf91744a2ef34d1ffcd7" UNIQUE ("ordemPagamentoAgrupadoHistoricoId")`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" DROP COLUMN "dataPagamento"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ADD "dataPagamento" TIMESTAMP`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" DROP COLUMN "dataReferencia"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ADD "dataReferencia" TIMESTAMP`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPago" TYPE numeric(10,5)`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPago" DROP NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPagar" TYPE numeric(10,5)`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPagar" DROP NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "saldoDevedor" TYPE numeric(10,5)`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "saldoDevedor" DROP NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" ADD CONSTRAINT "FK_DetalheA_OrdemPagamentoAgrupadoHistorico_OneToOne" FOREIGN KEY ("ordemPagamentoAgrupadoHistoricoId") REFERENCES "item_transacao_agrupado"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "detalhe_a" DROP CONSTRAINT "FK_DetalheA_OrdemPagamentoAgrupadoHistorico_OneToOne"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "saldoDevedor" SET NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "saldoDevedor" TYPE numeric`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPagar" SET NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPagar" TYPE numeric`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPago" SET NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ALTER COLUMN "valorPago" TYPE numeric`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" DROP COLUMN "dataReferencia"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ADD "dataReferencia" date`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" DROP COLUMN "dataPagamento"`);
+ await queryRunner.query(`ALTER TABLE "pagamento_indevido" ADD "dataPagamento" date NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" DROP CONSTRAINT "UQ_4acdbfdbf91744a2ef34d1ffcd7"`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" DROP COLUMN "ordemPagamentoAgrupadoHistoricoId"`);
+ await queryRunner.query(`ALTER TABLE "transacao_agrupado" ADD "isUnico" boolean`);
+ await queryRunner.query(`DROP TABLE "ordem_pagamento_agrupado_historico"`);
+ await queryRunner.query(`DROP TABLE "ordem_pagamento_agrupado"`);
+ await queryRunner.query(`DROP TABLE "ordem_pagamento"`);
+ }
+
+}
diff --git a/src/database/migrations/1738346663768-NovoRemessaImplantacao.ts b/src/database/migrations/1738346663768-NovoRemessaImplantacao.ts
new file mode 100644
index 000000000..b66e22b2e
--- /dev/null
+++ b/src/database/migrations/1738346663768-NovoRemessaImplantacao.ts
@@ -0,0 +1,22 @@
+import { MigrationInterface, QueryRunner } from "typeorm";
+
+export class NovoRemessaImplantacao1738346663768 implements MigrationInterface {
+ name = 'NovoRemessaImplantacao1738346663768'
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`DROP INDEX IF EXISTS "public"."idx_ordem_pagamento_agrupado"`);
+ await queryRunner.query(`DROP INDEX IF EXISTS "public"."idx_historico_data_referencia_id_agrupado"`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" DROP CONSTRAINT "FK_DetalheA_itemTransacaoAgrupado_OneToOne"`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" ALTER COLUMN "itemTransacaoAgrupadoId" DROP NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" ADD CONSTRAINT "FK_DetalheA_itemTransacaoAgrupado_OneToOne" FOREIGN KEY ("itemTransacaoAgrupadoId") REFERENCES "item_transacao_agrupado"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`ALTER TABLE "detalhe_a" DROP CONSTRAINT "FK_DetalheA_itemTransacaoAgrupado_OneToOne"`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" ALTER COLUMN "itemTransacaoAgrupadoId" SET NOT NULL`);
+ await queryRunner.query(`ALTER TABLE "detalhe_a" ADD CONSTRAINT "FK_DetalheA_itemTransacaoAgrupado_OneToOne" FOREIGN KEY ("itemTransacaoAgrupadoId") REFERENCES "item_transacao_agrupado"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
+ await queryRunner.query(`CREATE INDEX "idx_historico_data_referencia_id_agrupado" ON "ordem_pagamento_agrupado_historico" ("dataReferencia", "ordemPagamentoAgrupadoId") `);
+ await queryRunner.query(`CREATE INDEX "idx_ordem_pagamento_agrupado" ON "ordem_pagamento" ("ordemPagamentoAgrupadoId") `);
+ }
+
+}
diff --git a/src/database/migrations/migrations/1737463762168-CreateIndexDataReferenciaHistorico.ts b/src/database/migrations/migrations/1737463762168-CreateIndexDataReferenciaHistorico.ts
new file mode 100644
index 000000000..d8e259c12
--- /dev/null
+++ b/src/database/migrations/migrations/1737463762168-CreateIndexDataReferenciaHistorico.ts
@@ -0,0 +1,13 @@
+import { MigrationInterface, QueryRunner } from "typeorm"
+
+export class CreateIndexDataReferenciaHistorico1737463762168 implements MigrationInterface {
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`create index idx_historico_data_referencia_id_agrupado on ordem_pagamento_agrupado_historico("dataReferencia", "ordemPagamentoAgrupadoId");`)
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`drop index idx_historico_data_referencia_id_agrupado;`)
+ }
+
+}
diff --git a/src/relatorio/dtos/relatorio-consolidado-novo-remessa.dto.ts b/src/relatorio/dtos/relatorio-consolidado-novo-remessa.dto.ts
new file mode 100644
index 000000000..2a37873af
--- /dev/null
+++ b/src/relatorio/dtos/relatorio-consolidado-novo-remessa.dto.ts
@@ -0,0 +1,27 @@
+import { DeepPartial } from 'typeorm';
+
+export class RelatorioConsolidadoNovoRemessaDto {
+ constructor(consolidado?: DeepPartial) {
+ if (consolidado !== undefined) {
+ Object.assign(this, consolidado);
+ }
+ }
+
+ count: number;
+ valor: number;
+ data: RelatorioConsolidadoNovoRemessaData[];
+
+}
+
+export class RelatorioConsolidadoNovoRemessaData {
+ constructor(consolidado?: DeepPartial) {
+ if (consolidado !== undefined) {
+ Object.assign(this, consolidado);
+ }
+ }
+
+ valor: number;
+
+ nomefavorecido: string;
+
+}
\ No newline at end of file
diff --git a/src/relatorio/dtos/relatorio-sintetico-novo-remessa.dto.ts b/src/relatorio/dtos/relatorio-sintetico-novo-remessa.dto.ts
new file mode 100644
index 000000000..7d229d00e
--- /dev/null
+++ b/src/relatorio/dtos/relatorio-sintetico-novo-remessa.dto.ts
@@ -0,0 +1,53 @@
+import { Exclude } from 'class-transformer';
+import { SetValue } from 'src/utils/decorators/set-value.decorator';
+import { DeepPartial } from 'typeorm';
+
+export class RelatorioSinteticoNovoRemessaDto {
+ constructor(sintetico?: DeepPartial) {
+ if (sintetico !== undefined) {
+ Object.assign(this, sintetico);
+ }
+ }
+
+ count: number;
+ total: number;
+ data: RelatorioSinteticoNovoRemessaConsorcio[];
+
+}
+
+export class RelatorioSinteticoNovoRemessaConsorcio {
+ constructor(sintetico?: DeepPartial) {
+ if (sintetico !== undefined) {
+ Object.assign(this, sintetico);
+ }
+ }
+ subtotalConsorcio: number;
+ agrupamentoDia: RelatorioSinteticoNovoRemessaFavorecido[];
+}
+
+export class RelatorioSinteticoNovoRemessaFavorecido {
+ constructor(sintetico?: DeepPartial) {
+ if (sintetico !== undefined) {
+ Object.assign(this, sintetico);
+ }
+ }
+ subtotalFavorecido: number;
+ agrupamentoDia: RelatorioSinteticoNovoRemessaDia[];
+}
+
+
+export class RelatorioSinteticoNovoRemessaDia {
+ constructor(sintetico?: DeepPartial) {
+ if (sintetico !== undefined) {
+ Object.assign(this, sintetico);
+ }
+ }
+ consorcio: string;
+ valor: number;
+ favorecido: string;
+ cpfCnpj: string;
+ status: string;
+ mensagemStatus: string;
+ dataPagamento: string;
+}
+
diff --git a/src/relatorio/interfaces/find-publicacao-relatorio-novo-remessa.interface.ts b/src/relatorio/interfaces/find-publicacao-relatorio-novo-remessa.interface.ts
new file mode 100644
index 000000000..874044872
--- /dev/null
+++ b/src/relatorio/interfaces/find-publicacao-relatorio-novo-remessa.interface.ts
@@ -0,0 +1,12 @@
+export interface IFindPublicacaoRelatorioNovoRemessa {
+ dataInicio: Date;
+ dataFim: Date;
+ userIds?: number[];
+ consorcioNome?: string[];
+ valorMin?: number;
+ valorMax?: number;
+ pago?: boolean;
+ aPagar?: boolean;
+ emProcessamento?:boolean;
+ erro?: boolean;
+}
\ No newline at end of file
diff --git a/src/relatorio/interfaces/find-publicacao-relatorio.interface.ts b/src/relatorio/interfaces/find-publicacao-relatorio.interface.ts
index 542884756..ab53c4efd 100644
--- a/src/relatorio/interfaces/find-publicacao-relatorio.interface.ts
+++ b/src/relatorio/interfaces/find-publicacao-relatorio.interface.ts
@@ -1,11 +1,12 @@
export interface IFindPublicacaoRelatorio {
dataInicio: Date;
dataFim: Date;
- favorecidoNome?: string[];
+ favorecidoNome?: string[];
consorcioNome?: string[];
valorMin?: number;
valorMax?: number;
pago?: boolean;
aPagar?: boolean;
- emProcessamento?:boolean;
+ emProcessamento?: boolean;
+ eleicao?: boolean;
}
\ No newline at end of file
diff --git a/src/relatorio/relatorio-consolidado.repository.ts b/src/relatorio/relatorio-consolidado.repository.ts
index 09de8278c..103855871 100644
--- a/src/relatorio/relatorio-consolidado.repository.ts
+++ b/src/relatorio/relatorio-consolidado.repository.ts
@@ -5,20 +5,21 @@ import { RelatorioConsolidadoDto } from './dtos/relatorio-consolidado.dto';
import { IFindPublicacaoRelatorio } from './interfaces/find-publicacao-relatorio.interface';
import { CustomLogger } from 'src/utils/custom-logger';
-
@Injectable()
-export class RelatorioConsolidadoRepository {
-
- constructor(@InjectDataSource()
- private readonly dataSource: DataSource) {}
-
- private logger = new CustomLogger(RelatorioConsolidadoRepository.name, { timestamp: true });
-
- private getQueryAPagarConsorcio(dataInicio:string,dataFim:string,
- valorMin?:number,valorMax?:number,nomeConsorcio?:string[]){
+export class RelatorioConsolidadoRepository {
+ constructor(
+ @InjectDataSource()
+ private readonly dataSource: DataSource,
+ ) { }
+
+ private logger = new CustomLogger(RelatorioConsolidadoRepository.name, { timestamp: true });
+
+ private getQueryAPagarConsorcio(dataInicio: string, dataFim: string, valorMin?: number, valorMax?: number, nomeConsorcio?: string[], eleicao?: boolean) {
let query = ` select * from ( `;
query = query + `select cs."consorcio" nomeFavorecido,sum(cs."valor_agrupado")::float valor from ( `;
- query = query + ` select distinct tv.id AS id,
+ query =
+ query +
+ ` select distinct tv.id AS id,
tv."nomeConsorcio" AS consorcio,
tv."valorPago" AS valor_agrupado
from transacao_view tv
@@ -26,85 +27,136 @@ export class RelatorioConsolidadoRepository {
and tv."valorPago" is not null
and tv."valorPago" >0 `;
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio)))
- query = query +` and tv."datetimeTransacao" between '${dataInicio+' 00:00:00'}' and '${dataFim+' 23:59:59'}' `;
+ if (dataInicio !== undefined && dataFim !== undefined && (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))) query = query + ` and tv."datetimeTransacao" between '${dataInicio + ' 00:00:00'}' and '${dataFim + ' 23:59:59'}' `;
- if(['Todos'].some(i=>nomeConsorcio?.includes(i))) {
- query = query +` AND tv."nomeConsorcio" in ('STPC','STPL','VLT','Santa Cruz',
+ if (['Todos'].some((i) => nomeConsorcio?.includes(i))) {
+ query =
+ query +
+ ` AND tv."nomeConsorcio" in ('STPC','STPL','VLT','Santa Cruz',
'Internorte','Intersul','Transcarioca','MobiRio','TEC') `;
- }else if((nomeConsorcio!==undefined) && !(['Todos'].some(i=>nomeConsorcio?.includes(i))))
- query = query +` and tv."nomeConsorcio" in('${nomeConsorcio?.join("','")}')`;
-
- query = query + `) as cs `;
+ } else if (nomeConsorcio !== undefined && !['Todos'].some((i) => nomeConsorcio?.includes(i))) query = query + ` and tv."nomeConsorcio" in('${nomeConsorcio?.join("','")}')`;
+ if (eleicao !== undefined) {
- query = query + ` group by cs."consorcio"`
+ query += ` and tv."idOrdemPagamento" LIKE '%U%'`;
+ } else {
+
+ query += `and tv."idOrdemPagamento" NOT LIKE '%U%' `;
+ }
+ query = query + `) as cs `;
+
+ query = query + ` group by cs."consorcio"`;
query = query + ` order by cs."consorcio" `;
- query = query + `) as resul where (1=1) `;
-
- if(valorMin!==undefined)
- query = query +` and resul."valor">=${valorMin}`;
+ query = query + `) as resul where (1=1) `;
+
+ if (valorMin !== undefined) query = query + ` and resul."valor">=${valorMin}`;
- if(valorMax!==undefined)
- query = query + ` and resul."valor"<=${valorMax}`;
+ if (valorMax !== undefined) query = query + ` and resul."valor"<=${valorMax}`;
- this.logger.debug(query);
- return query;
- }
-
- private getQueryConsorcio(dataInicio:string,dataFim:string,pago?:boolean,
- valorMin?:number,valorMax?:number,nomeConsorcio?:string[],emProcessamento?:boolean){
+ this.logger.debug(query);
+ return query;
+ }
+
+ private getQueryConsorcio(
+ dataInicio: string,
+ dataFim: string,
+ pago?: boolean,
+ valorMin?: number,
+ valorMax?: number,
+ nomeConsorcio?: string[],
+ emProcessamento?: boolean,
+ eleicao?: boolean
+ ) {
let query = ` select * from ( `;
- query = query +` select cs."consorcio" nomeFavorecido,sum(cs."valor_agrupado")::float valor from ( `;
- query = query +` select distinct ita.id AS id,
+ query += ` select cs."consorcio" nomeFavorecido,sum(cs."valor_agrupado")::float valor from ( `;
+ query += `
+ select distinct ita.id AS id,
ita."nomeConsorcio" AS consorcio,
da."valorLancamento" AS valor_agrupado
- from transacao_agrupado ta
- inner join item_transacao_agrupado ita on ita."transacaoAgrupadoId"=ta."id"
- inner join detalhe_a da on da."itemTransacaoAgrupadoId"= ita.id
- inner join item_transacao it on ita.id = it."itemTransacaoAgrupadoId"
- inner join arquivo_publicacao ap on ap."itemTransacaoId"=it.id
- where ta."statusId"<>5 `;
-
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio)))
- query = query + ` and da."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
-
- if((nomeConsorcio!==undefined) && !(['Todos'].some(i=>nomeConsorcio?.includes(i))))
- query = query +` and ita."nomeConsorcio" in('${nomeConsorcio?.join("','")}')`;
+ from transacao_agrupado ta
+ inner join item_transacao_agrupado ita on ita."transacaoAgrupadoId"=ta."id"
+ inner join detalhe_a da on da."itemTransacaoAgrupadoId"= ita.id
+ inner join item_transacao it on ita.id = it."itemTransacaoAgrupadoId"
+ inner join arquivo_publicacao ap on ap."itemTransacaoId"=it.id
+ where ta."statusId"<>5 `;
+
+ if (
+ dataInicio !== undefined &&
+ dataFim !== undefined &&
+ (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))
+ ) {
+ query += ` and da."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
+ }
+
+ if (nomeConsorcio !== undefined && !['Todos'].some((i) => nomeConsorcio?.includes(i))) {
+ query += ` and ita."nomeConsorcio" in('${nomeConsorcio?.join("','")}')`;
+ }
- if(emProcessamento==true){
- query = query + ` and ap."isPago"=false and TRIM(da."ocorrenciasCnab")='' `
- }else if(pago!==undefined){
- query = query + ` and ap."isPago"=${pago} and TRIM(da."ocorrenciasCnab")<>'' `;
- }
+ if (emProcessamento === true) {
+ if (pago === true) {
+ query += `
+ and (
+ ap."isPago" = true
+ OR (ap."isPago" = false AND TRIM(da."ocorrenciasCnab") = '')
+ )
+ `
+ } else if (pago === false) {
+ query += `
+ and (
+ ap."isPago" = false
+ AND (
+ TRIM(da."ocorrenciasCnab") = ''
+ OR TRIM(da."ocorrenciasCnab") <> ''
+ )
+ )
+ `
+ } else {
+ query += `
+ and ap."isPago" = false
+ AND TRIM(da."ocorrenciasCnab") = ''
+ `
+ }
+ } else if (pago !== undefined) {
+ query += `
+ and ap."isPago" = ${pago}
+ AND TRIM(da."ocorrenciasCnab") <> ''
+ `
+ }
+ if (eleicao !== undefined) {
+
+ query += ` and ita."idOrdemPagamento" LIKE '%U%'`;
+ } else {
+
+ query += `and ita."idOrdemPagamento" NOT LIKE '%U%' `;
+ }
+ query += `) as cs `;
+ query += ` group by cs."consorcio"`;
+ query += ` order by cs."consorcio" `;
+ query += `) as resul where (1=1) `;
- query = query + `) as cs `;
+ if (valorMin !== undefined) {
+ query += ` and resul."valor">=${valorMin}`;
+ }
- query = query + ` group by cs."consorcio"`
+ if (valorMax !== undefined) {
+ query += ` and resul."valor"<=${valorMax}`;
+ }
- query = query + ` order by cs."consorcio" `;
- query = query + `) as resul where (1=1) `;
-
- if(valorMin!==undefined)
- query = query +` and resul."valor">=${valorMin}`;
- if(valorMax!==undefined)
- query = query + ` and resul."valor"<=${valorMax}`;
-
- return query;
+ return query;
}
-
- private getQueryAPagarOperadores(dataInicio:string,dataFim:string,valorMin?:number,
- valorMax?:number,favorecidoNome?:string[]){
+
+
+ private getQueryAPagarOperadores(dataInicio: string, dataFim: string, valorMin?: number, valorMax?: number, favorecidoNome?: string[], eleicao?: boolean) {
let query = `select * from ( `;
query = query + ` select cs."favorecido" nomeFavorecido,sum(cs."valor_agrupado")::float valor from ( `;
- query = query + ` select distinct tv.id AS id,
+ query =
+ query +
+ ` select distinct tv.id AS id,
tv."nomeConsorcio" AS consorcio,
cf.nome AS favorecido,
cf."cpfCnpj" AS favorecido_cpfcnpj,
@@ -114,39 +166,55 @@ export class RelatorioConsolidadoRepository {
where tv."itemTransacaoAgrupadoId" is null
and tv."valorPago" is not null
and tv."valorPago" >0 `;
-
- query = query +` and tv."datetimeTransacao" between '${dataInicio+' 00:00:00'}' and '${dataFim+' 23:59:59'}' `;
-
- if(favorecidoNome!==undefined && !(['Todos'].some(i=>favorecidoNome?.includes(i))))
- query = query +` and cf.nome in('${favorecidoNome?.join("','")}')`;
- query = query + ` ) as cs `;
+ query = query + ` and tv."datetimeTransacao" between '${dataInicio + ' 00:00:00'}' and '${dataFim + ' 23:59:59'}' `;
+
+ if (favorecidoNome !== undefined && !['Todos'].some((i) => favorecidoNome?.includes(i))) query = query + ` and cf.nome in('${favorecidoNome?.join("','")}')`;
+ if (eleicao !== undefined) {
+
+ query += ` and ita."idOrdemPagamento" LIKE '%U%'`;
+ } else {
+
+ query += `and ita."idOrdemPagamento" NOT LIKE '%U%' `;
+ }
+ query = query + ` ) as cs `;
query = query + ` group by cs."consorcio", cs."favorecido" `;
query = query + ` order by cs."favorecido" `;
- query = query + `) as resul where (1=1) `;
-
- if(valorMin!==undefined)
- query = query +` and resul."valor">=${valorMin}`;
+ query = query + `) as resul where (1=1) `;
+
+ if (valorMin !== undefined) query = query + ` and resul."valor">=${valorMin}`;
+
+ if (valorMax !== undefined) query = query + ` and resul."valor"<=${valorMax}`;
+
- if(valorMax!==undefined)
- query = query + ` and resul."valor"<=${valorMax}`;
this.logger.debug(query);
return query;
}
-
- private getQueryOperadores(dataInicio:string,dataFim:string,pago?:boolean,valorMin?:number,
- valorMax?:number,favorecidoNome?:string[],emProcessamento?:boolean){
- let query = ` select * from ( `;
- query = query + ` select cs."favorecido" nomeFavorecido,sum(cs."valor_agrupado")::float valor from ( `;
- query = query + ` select distinct ita.id AS id,
+
+ private getQueryOperadores(dataInicio: string, dataFim: string, pago?: boolean, valorMin?: number, valorMax?: number, favorecidoNome?: string[], emProcessamento?: boolean, eleicao?: boolean) {
+ let valor = '';
+ if(eleicao){
+ if(pago){
+ valor = 'da."valorRealEfetivado"'
+ } else {
+ valor = ' da."valorLancamento"'
+ }
+ } else {
+ valor = ' da."valorLancamento"'
+ }
+ let query = ` select * from ( `;
+ query = query + ` select cs."favorecido" nomeFavorecido,sum(cs."valor_agrupado")::float valor from ( `;
+ query =
+ query +
+ ` select distinct ita.id AS id,
ita."nomeConsorcio" AS consorcio,
cf.nome AS favorecido,
cf."cpfCnpj" AS favorecido_cpfcnpj,
- da."valorLancamento" AS valor_agrupado
+ ${valor} AS valor_agrupado
from transacao_agrupado ta
inner join item_transacao_agrupado ita on ita."transacaoAgrupadoId"=ta."id"
inner join detalhe_a da on da."itemTransacaoAgrupadoId"= ita.id
@@ -154,66 +222,77 @@ export class RelatorioConsolidadoRepository {
inner join arquivo_publicacao ap on ap."itemTransacaoId"=it.id
inner join cliente_favorecido cf on cf.id=it."clienteFavorecidoId"
where ta."statusId"<>5 and ita."nomeConsorcio" in('STPC','STPL','TEC') `;
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio)))
- query = query +` and da."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
- if(emProcessamento==true){
- query = query + ` and ap."isPago"=false and TRIM(da."ocorrenciasCnab")='' `
- }else if(pago!==undefined){
- query = query + ` and ap."isPago"=${pago} and TRIM(da."ocorrenciasCnab")<>'' `;
- }
-
-
- if(favorecidoNome!==undefined && !(['Todos'].some(i=>favorecidoNome?.includes(i))))
- query = query +` and cf.nome in('${favorecidoNome?.join("','")}')`;
-
- query = query + `) as cs `;
-
- query = query + ` group by cs."consorcio", cs."favorecido" `;
-
- query = query + ` order by cs."favorecido" `;
-
- query = query + `) as resul where (1=1) `;
-
- if(valorMin!==undefined)
- query = query +` and resul."valor">=${valorMin}`;
-
- if(valorMax!==undefined)
- query = query + ` and resul."valor"<=${valorMax}`;
+ if (dataInicio !== undefined && dataFim !== undefined && (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))) query = query + ` and da."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
+ if (emProcessamento == true) {
+ query = query + ` and ap."isPago"=false and TRIM(da."ocorrenciasCnab")='' `;
+ } else if (pago !== undefined) {
+ query = query + ` and ap."isPago"=${pago} and TRIM(da."ocorrenciasCnab")<>'' `;
+ }
+
+ if (favorecidoNome !== undefined && !['Todos'].some((i) => favorecidoNome?.includes(i))) query = query + ` and cf.nome in('${favorecidoNome?.join("','")}')`;
+ console.log(eleicao)
+
+ if (eleicao !== undefined) {
+
+ query += ` and ita."idOrdemPagamento" LIKE '%U%'`;
+ } else {
+
+ query += `and ita."idOrdemPagamento" NOT LIKE '%U%' `;
+ }
+ query = query + `) as cs `;
+
+ query = query + ` group by cs."consorcio", cs."favorecido" `;
+
+ query = query + ` order by cs."favorecido" `;
+
+ query = query + `) as resul where (1=1) `;
+
+ if (valorMin !== undefined) query = query + ` and resul."valor">=${valorMin}`;
+
+ if (valorMax !== undefined) query = query + ` and resul."valor"<=${valorMax}`;
return query;
}
- public async findConsolidado(args: IFindPublicacaoRelatorio): Promise {
+ public async findConsolidado(args: IFindPublicacaoRelatorio): Promise {
+
let queryConsorcio = '';
- if(args.aPagar === true && args.favorecidoNome ===undefined){
- queryConsorcio = this.getQueryAPagarConsorcio(args.dataInicio.toISOString().slice(0,10),
- args.dataFim.toISOString().slice(0,10),args.valorMin,
- args.valorMax,args.consorcioNome);
- }
-
- if((args.aPagar === undefined || args.aPagar === false) &&
- (args.consorcioNome!==undefined || args.favorecidoNome === undefined)){
- queryConsorcio = this.getQueryConsorcio(args.dataInicio.toISOString().slice(0,10),
- args.dataFim.toISOString().slice(0,10),args.pago,args.valorMin,
- args.valorMax,args.consorcioNome,args.emProcessamento);
- }
-
- let queryOperadores ='';
- if(args.aPagar === true && args.consorcioNome===undefined){
- queryOperadores = this.getQueryAPagarOperadores(args.dataInicio.toISOString().slice(0,10),
- args.dataFim.toISOString().slice(0,10),args.valorMin,
- args.valorMax,args.favorecidoNome);
- }
-
- if((args.aPagar === undefined || args.aPagar === false) && (args.consorcioNome===undefined || args.favorecidoNome !==undefined)) {
- queryOperadores = this.getQueryOperadores(args.dataInicio.toISOString().slice(0,10),
- args.dataFim.toISOString().slice(0,10),args.pago,args.valorMin,args.valorMax,args.favorecidoNome,args.emProcessamento);
+ let queryOperadores = '';
+ if (!args.favorecidoNome && !args.consorcioNome) {
+
+ queryOperadores = this.getQueryOperadores(
+ args.dataInicio.toISOString().slice(0, 10),
+ args.dataFim.toISOString().slice(0, 10),
+ args.pago,
+ args.valorMin,
+ args.valorMax,
+ args.favorecidoNome,
+ args.emProcessamento,
+ args.eleicao
+ );
+ } else {
+ if (args.aPagar === true && args.favorecidoNome === undefined) {
+ queryConsorcio = this.getQueryAPagarConsorcio(args.dataInicio.toISOString().slice(0, 10), args.dataFim.toISOString().slice(0, 10), args.valorMin, args.valorMax, args.consorcioNome, args.eleicao);
+ }
+
+ if ((args.aPagar === undefined || args.aPagar === false) && (args.consorcioNome !== undefined || args.favorecidoNome === undefined)) {
+
+ queryConsorcio = this.getQueryConsorcio(args.dataInicio.toISOString().slice(0, 10), args.dataFim.toISOString().slice(0, 10), args.pago, args.valorMin, args.valorMax, args.consorcioNome, args.emProcessamento, args.eleicao);
+ }
+
+ if (args.aPagar === true && args.consorcioNome === undefined) {
+ queryOperadores = this.getQueryAPagarOperadores(args.dataInicio.toISOString().slice(0, 10), args.dataFim.toISOString().slice(0, 10), args.valorMin, args.valorMax, args.favorecidoNome, args.eleicao);
+ }
+
+ if ((args.aPagar === undefined || args.aPagar === false) && (args.consorcioNome === undefined || args.favorecidoNome !== undefined)) {
+ queryOperadores = this.getQueryOperadores(args.dataInicio.toISOString().slice(0, 10), args.dataFim.toISOString().slice(0, 10), args.pago, args.valorMin, args.valorMax, args.favorecidoNome, args.emProcessamento, args.eleicao);
+ }
+
+ if (queryConsorcio !== '' && queryOperadores !== '') {
+ queryOperadores = ` union all ` + queryOperadores;
+ }
}
-
- if(queryConsorcio !=='' && queryOperadores!==''){
- queryOperadores = ` union all `+queryOperadores;
- }
-
+
+
const query = queryConsorcio + queryOperadores;
this.logger.debug(query);
const queryRunner = this.dataSource.createQueryRunner();
@@ -222,5 +301,5 @@ export class RelatorioConsolidadoRepository {
queryRunner.release();
const consolidados = result.map((r) => new RelatorioConsolidadoDto(r));
return consolidados;
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/src/relatorio/relatorio-novo-remessa.controller.ts b/src/relatorio/relatorio-novo-remessa.controller.ts
new file mode 100644
index 000000000..df1939598
--- /dev/null
+++ b/src/relatorio/relatorio-novo-remessa.controller.ts
@@ -0,0 +1,103 @@
+import { Body, Controller, Get, HttpCode, HttpException, HttpStatus, ParseArrayPipe, Query, UseGuards } from '@nestjs/common';
+import { AuthGuard } from '@nestjs/passport';
+import { ApiBearerAuth, ApiQuery, ApiTags } from '@nestjs/swagger';
+import { ApiDescription } from 'src/utils/api-param/description-api-param';
+import { ParseBooleanPipe } from 'src/utils/pipes/parse-boolean.pipe';
+import { ParseDatePipe } from 'src/utils/pipes/parse-date.pipe';
+import { ParseNumberPipe } from 'src/utils/pipes/parse-number.pipe';
+import { RelatorioService } from './relatorio.service';
+import { Int32 } from 'typeorm';
+import { RelatorioNovoRemessaService } from './relatorio-novo-remessa.service';
+
+@ApiTags('Cnab')
+@Controller({
+ path: 'cnab/relatorio-novo-remessa',
+ version: '1',
+})
+export class RelatorioNovoRemessaController {
+ constructor(private relatorioNovoRemessaService: RelatorioNovoRemessaService) {}
+
+ @ApiQuery({ name: 'dataInicio', description: 'Data da Ordem de Pagamento Inicial', required: true, type: String })
+ @ApiQuery({ name: 'dataFim', description: 'Data da Ordem de Pagamento Final', required: true, type: String })
+ @ApiQuery({ name: 'userIds', description: 'Pesquisa o id dos usuários.', required: false, type: [Number] })
+ @ApiQuery({ name: 'consorcioNome', description: ApiDescription({ _: 'Pesquisa o nome exato dos consórcios, sem distinção de acento ou maiúsculas.', 'STPC/STPL': 'Agrupa todos os vanzeiros sob o consórcio' }), required: false, type: [String] })
+ @ApiQuery({ name: 'valorMin', description: 'Somatório do valor bruto.', required: false, type: Number })
+ @ApiQuery({ name: 'valorMax', description: 'Somatório do valor bruto.', required: false, type: Number })
+ @ApiQuery({ name: 'pago', required: false, type: Boolean, description: ApiDescription({ _: 'Se o pagamento foi pago com sucesso.', default: false }) })
+ @ApiQuery({ name: 'aPagar', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for a pagar', default: false }) })
+ @ApiQuery({ name: 'emProcessamento', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for a emProcessamento', default: false }) })
+ @ApiQuery({ name: 'erro', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status do pagamento é de erro', default: false }) })
+ @ApiBearerAuth()
+ @UseGuards(AuthGuard('jwt'))
+ @Get('consolidado')
+ async getConsolidado(
+ @Query('dataInicio', new ParseDatePipe({ dateOnly: true }))
+ dataInicio: Date,
+ @Query('dataFim', new ParseDatePipe({ dateOnly: true }))
+ dataFim: Date,
+ @Query('userIds', new ParseArrayPipe({ items: Int32, separator: ',', optional: true }))
+ userIds: number[],
+ @Query('consorcioNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
+ consorcioNome: string[],
+ @Query('valorMin', new ParseNumberPipe({ optional: true }))
+ valorMin: number | undefined,
+ @Query('valorMax', new ParseNumberPipe({ optional: true }))
+ valorMax: number | undefined,
+ @Query('pago',new ParseBooleanPipe({ optional: true })) pago: boolean | undefined,
+ @Query('aPagar',new ParseBooleanPipe({ optional: true })) aPagar: boolean | undefined,
+ @Query('emProcessamento',new ParseBooleanPipe({ optional: true })) emProcessamento: boolean | undefined,
+ @Query('erro',new ParseBooleanPipe({ optional: true })) erro: boolean | undefined,
+){
+ try{
+ const result = await this.relatorioNovoRemessaService.findConsolidado({
+ dataInicio,dataFim, userIds, consorcioNome, valorMin, valorMax, pago, aPagar,emProcessamento, erro
+ });
+ return result;
+ }catch(e){
+ return new HttpException({ error: e.message}, HttpStatus.BAD_REQUEST);
+ }
+ }
+
+ @ApiQuery({ name: 'dataInicio', description: 'Data da Ordem de Pagamento Inicial', required: true, type: String })
+ @ApiQuery({ name: 'dataFim', description: 'Data da Ordem de Pagamento Final', required: true, type: String })
+ @ApiQuery({ name: 'userId', description: 'Pesquisa o id dos usuários.', required: false, type: String })
+ @ApiQuery({ name: 'consorcioNome', description: ApiDescription({ _: 'Pesquisa o nome parcial dos consórcios, sem distinção de acento ou maiúsculas.', 'STPC/STPL': 'Agrupa todos os vanzeiros sob o consórcio' }), required: false, type: String })
+ @ApiQuery({ name: 'valorMin', description: 'Somatório do valor bruto.', required: false, type: Number })
+ @ApiQuery({ name: 'valorMax', description: 'Somatório do valor bruto.', required: false, type: Number })
+ @ApiQuery({ name: 'pago', required: false, type: Boolean, description: ApiDescription({ _: 'Se o pagamento foi pago com sucesso.', default: false }) })
+ @ApiQuery({ name: 'aPagar', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for a pagar', default: false }) })
+ @ApiQuery({ name: 'emProcessamento', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for a emProcessamento', default: false }) })
+ @ApiQuery({ name: 'erro', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status do pagamento é de erro', default: false }) })
+ @HttpCode(HttpStatus.OK)
+ @ApiBearerAuth()
+ @UseGuards(AuthGuard('jwt'))
+ @Get('sintetico')
+ async getSintetico(
+ @Query('dataInicio', new ParseDatePipe({ dateOnly: true }))
+ dataInicio: Date,
+ @Query('dataFim', new ParseDatePipe({ dateOnly: true }))
+ dataFim: Date,
+ @Query('favorecidoNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
+ favorecidoNome: string[],
+ @Query('consorcioNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
+ consorcioNome: string[],
+ @Query('valorMin', new ParseNumberPipe({ optional: true }))
+ valorMin: number | undefined,
+ @Query('valorMax', new ParseNumberPipe({ optional: true }))
+ valorMax: number | undefined,
+ @Query('pago',new ParseBooleanPipe({ optional: true })) pago: boolean | undefined,
+ @Query('aPagar',new ParseBooleanPipe({ optional: true })) aPagar: boolean | undefined,
+ @Query('emProcessamento',new ParseBooleanPipe({ optional: true })) emProcessamento: boolean | undefined,
+ @Query('erro',new ParseBooleanPipe({ optional: true })) erro: boolean | undefined
+ ) {
+ try {
+ const result = await this.relatorioNovoRemessaService.findSintetico({
+ dataInicio,dataFim, favorecidoNome, consorcioNome, valorMin, valorMax, pago, aPagar,emProcessamento
+ });
+ return result;
+ }catch(e){
+ return new HttpException({ error: e.message}, HttpStatus.BAD_REQUEST);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/relatorio/relatorio-novo-remessa.repository.ts b/src/relatorio/relatorio-novo-remessa.repository.ts
new file mode 100644
index 000000000..0d139d7a9
--- /dev/null
+++ b/src/relatorio/relatorio-novo-remessa.repository.ts
@@ -0,0 +1,177 @@
+import { Injectable } from '@nestjs/common';
+import { InjectDataSource } from '@nestjs/typeorm';
+import { DataSource } from 'typeorm';
+import { CustomLogger } from 'src/utils/custom-logger';
+import { IFindPublicacaoRelatorioNovoRemessa } from './interfaces/find-publicacao-relatorio-novo-remessa.interface';
+import {
+ RelatorioConsolidadoNovoRemessaData,
+ RelatorioConsolidadoNovoRemessaDto,
+} from './dtos/relatorio-consolidado-novo-remessa.dto';
+import { parseNumber } from '../cnab/utils/cnab/cnab-field-utils';
+
+@Injectable()
+export class RelatorioNovoRemessaRepository {
+ private static readonly QUERY_CONSOLIDADO = `
+ select op."userId", u."fullName", sum(op.valor) as "valorTotal"
+ from ordem_pagamento op
+ inner join public.ordem_pagamento_agrupado opa on op."ordemPagamentoAgrupadoId" = opa.id
+ join lateral (
+ select opah."dataReferencia",
+ opah."statusRemessa",
+ opah."motivoStatusRemessa",
+ opah."ordemPagamentoAgrupadoId",
+ opah."userBankCode",
+ opah."userBankAgency",
+ opah."userBankAccount",
+ opah."userBankAccountDigit"
+ from ordem_pagamento_agrupado_historico opah
+ where opa.id = opah."ordemPagamentoAgrupadoId"
+ and opah."dataReferencia" = (select max("dataReferencia") from ordem_pagamento_agrupado_historico where "ordemPagamentoAgrupadoId" = opa.id)
+ ) opah on opah."ordemPagamentoAgrupadoId" = opa.id
+ inner join "user" u on op."userId" = u.id
+ and ("userId" = any($1) or $1 is null)
+ and (date_trunc('day', op."dataCaptura") BETWEEN $2 and $3 or $2 is null or $3 is null)
+ and ("statusRemessa" = any($4) or $4 is null)
+ and (trim(upper("nomeConsorcio")) = any($5) or $5 is null)
+ group by op."userId", u."fullName"
+ having (sum(op.valor) >= $6 or $6 is null)
+ and (sum(op.valor) <= $7 or $7 is null)
+ order by u."fullName"
+ `;
+
+ private static readonly QUERY_SINTETICO = `
+ select op."userId", u."fullName", op.valor,
+ CASE opah."statusRemessa"
+ WHEN 3 THEN 'pago'
+ WHEN 4 THEN 'naopago'
+ ELSE 'apagar'
+ END as status,
+ opah."motivoStatusRemessa"
+ from ordem_pagamento op
+ inner join public.ordem_pagamento_agrupado opa on op."ordemPagamentoAgrupadoId" = opa.id
+ join lateral (
+ select opah."dataReferencia",
+ opah."statusRemessa",
+ opah."motivoStatusRemessa",
+ opah."ordemPagamentoAgrupadoId",
+ opah."userBankCode",
+ opah."userBankAgency",
+ opah."userBankAccount",
+ opah."userBankAccountDigit"
+ from ordem_pagamento_agrupado_historico opah
+ where opa.id = opah."ordemPagamentoAgrupadoId"
+ and opah."dataReferencia" = (select max("dataReferencia") from ordem_pagamento_agrupado_historico where "ordemPagamentoAgrupadoId" = opa.id)
+ ) opah on opah."ordemPagamentoAgrupadoId" = opa.id
+ inner join "user" u on op."userId" = u.id
+ and ("userId" = any($1) or $1 is null)
+ and (date_trunc('day', op."dataCaptura") BETWEEN $2 and $3 or $2 is null or $3 is null)
+ and ("statusRemessa" = any($4) or $4 is null)
+ and (trim(upper("nomeConsorcio")) = any($5) or $5 is null)
+ and (op.valor >= $6 or $6 is null)
+ and (op.valor <= $7 or $7 is null)
+ order by u."fullName"`;
+
+ constructor(
+ @InjectDataSource()
+ private readonly dataSource: DataSource,
+ ) {}
+ private logger = new CustomLogger(RelatorioNovoRemessaRepository.name, { timestamp: true });
+
+ public async findConsolidado(filter: IFindPublicacaoRelatorioNovoRemessa): Promise {
+ this.logger.debug(RelatorioNovoRemessaRepository.QUERY_CONSOLIDADO);
+
+ if (filter.consorcioNome) {
+ filter.consorcioNome = filter.consorcioNome.map((c) => { return c.toUpperCase().trim();});
+ }
+
+ const parameters =
+ [
+ filter.userIds || null,
+ filter.dataInicio || null,
+ filter.dataFim || null,
+ this.getStatusParaFiltro(filter),
+ filter.consorcioNome || null,
+ filter.valorMin || null,
+ filter.valorMax || null
+ ];
+
+ const queryRunner = this.dataSource.createQueryRunner();
+ await queryRunner.connect();
+ const result: any[] = await queryRunner.query(RelatorioNovoRemessaRepository.QUERY_CONSOLIDADO, parameters);
+ await queryRunner.release();
+ const count = result.length;
+ const valorTotal = result.reduce((acc, curr) => acc + parseFloat(curr.valorTotal), 0);
+ const relatorioConsolidadoDto = new RelatorioConsolidadoNovoRemessaDto();
+ relatorioConsolidadoDto.valor = parseFloat(valorTotal);
+ relatorioConsolidadoDto.count = count;
+ relatorioConsolidadoDto.data = result
+ .map((r) => {
+ const elem = new RelatorioConsolidadoNovoRemessaData();
+ elem.nomefavorecido = r.fullName;
+ elem.valor = parseFloat(r.valorTotal);
+ return elem;
+ });
+ return relatorioConsolidadoDto;
+ }
+
+ public async findSintetico(filter: IFindPublicacaoRelatorioNovoRemessa): Promise {
+ this.logger.debug(RelatorioNovoRemessaRepository.QUERY_SINTETICO);
+
+ if (filter.consorcioNome) {
+ filter.consorcioNome = filter.consorcioNome.map((c) => { return c.toUpperCase().trim();});
+ }
+
+ const parameters =
+ [
+ filter.userIds || null,
+ filter.dataInicio || null,
+ filter.dataFim || null,
+ this.getStatusParaFiltro(filter),
+ filter.consorcioNome || null,
+ filter.valorMin || null,
+ filter.valorMax || null
+ ];
+
+ const queryRunner = this.dataSource.createQueryRunner();
+ await queryRunner.connect();
+ const result: any[] = await queryRunner.query(RelatorioNovoRemessaRepository.QUERY_SINTETICO, parameters);
+ await queryRunner.release();
+ const count = result.length;
+ const valorTotal = result.reduce((acc, curr) => acc + parseFloat(curr.valorTotal), 0);
+ const relatorioConsolidadoDto = new RelatorioConsolidadoNovoRemessaDto();
+ relatorioConsolidadoDto.valor = parseFloat(valorTotal);
+ relatorioConsolidadoDto.count = count;
+ relatorioConsolidadoDto.data = result
+ .map((r) => {
+ const elem = new RelatorioConsolidadoNovoRemessaData();
+ elem.nomefavorecido = r.fullName;
+ elem.valor = parseFloat(r.valorTotal);
+ return elem;
+ });
+ return relatorioConsolidadoDto;
+ }
+
+ private getStatusParaFiltro(filter: IFindPublicacaoRelatorioNovoRemessa) {
+ let statuses: number[] | null = null;
+ if (filter.emProcessamento || filter.pago || filter.erro || filter.aPagar) {
+ statuses = [];
+
+ if (filter.aPagar) {
+ statuses.push(0);
+ statuses.push(1);
+ }
+ if (filter.emProcessamento) {
+ statuses.push(2);
+ }
+
+ if (filter.pago) {
+ statuses.push(3);
+ }
+
+ if (filter.erro) {
+ statuses.push(4);
+ }
+ }
+ return statuses;
+ }
+}
\ No newline at end of file
diff --git a/src/relatorio/relatorio-novo-remessa.service.ts b/src/relatorio/relatorio-novo-remessa.service.ts
new file mode 100644
index 000000000..0d8548829
--- /dev/null
+++ b/src/relatorio/relatorio-novo-remessa.service.ts
@@ -0,0 +1,42 @@
+import { Injectable } from '@nestjs/common';
+import { IFindPublicacaoRelatorio } from './interfaces/find-publicacao-relatorio.interface';
+import { RelatorioConsolidadoRepository } from './relatorio-consolidado.repository';
+import { RelatorioConsolidadoResultDto } from './dtos/relatorio-consolidado-result.dto';
+import { RelatorioAnaliticoResultDto } from './dtos/relatorio-analitico-result.dto';
+import { RelatorioSinteticoResultDto } from './dtos/relatorio-sintetico-result.dto';
+import { RelatorioAnaliticoRepository } from './relatorio-analitico.repository';
+import { RelatorioSinteticoRepository } from './relatorio-sintetico.repository';
+import { IFindPublicacaoRelatorioNovoRemessa } from './interfaces/find-publicacao-relatorio-novo-remessa.interface';
+import { RelatorioConsolidadoNovoRemessaDto } from './dtos/relatorio-consolidado-novo-remessa.dto';
+import { RelatorioNovoRemessaRepository } from './relatorio-novo-remessa.repository';
+
+@Injectable()
+export class RelatorioNovoRemessaService {
+ constructor(private relatorioNovoRemessaRepository: RelatorioNovoRemessaRepository,
+ ) {}
+
+ /**
+ * Gerar relatórios consolidados - agrupados por Favorecido.
+ */
+ async findConsolidado(args: IFindPublicacaoRelatorioNovoRemessa) {
+ if(args.dataInicio ===undefined || args.dataFim === undefined ||
+ new Date(args.dataFim) < new Date(args.dataInicio)){
+ throw new Error('Parametro de data inválido');
+ }
+
+ return this.relatorioNovoRemessaRepository.findConsolidado(args);
+ }
+
+ ///////SINTETICO //////
+
+ async findSintetico(args: IFindPublicacaoRelatorio){
+ if(args.dataInicio ===undefined || args.dataFim === undefined ||
+ new Date(args.dataFim) < new Date(args.dataInicio)){
+ throw new Error('Parametro de data inválido');
+ }
+
+ let result: RelatorioSinteticoResultDto[]=[];
+ return result;
+ }
+
+}
diff --git a/src/relatorio/relatorio-sintetico.repository.ts b/src/relatorio/relatorio-sintetico.repository.ts
index f4c1759a5..d6ba2d4bf 100644
--- a/src/relatorio/relatorio-sintetico.repository.ts
+++ b/src/relatorio/relatorio-sintetico.repository.ts
@@ -1,84 +1,83 @@
import { Injectable } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
-import { DataSource } from 'typeorm';
-import { IFindPublicacaoRelatorio } from './interfaces/find-publicacao-relatorio.interface';
import { CustomLogger } from 'src/utils/custom-logger';
+import { DataSource } from 'typeorm';
import { RelatorioSinteticoDto } from './dtos/relatorio-sintetico.dto';
-import { query } from 'express';
+import { IFindPublicacaoRelatorio } from './interfaces/find-publicacao-relatorio.interface';
+import { compactQuery } from 'src/utils/console-utils';
@Injectable()
-export class RelatorioSinteticoRepository {
-
- constructor(@InjectDataSource()
- private readonly dataSource: DataSource) {}
+export class RelatorioSinteticoRepository {
+ constructor(
+ @InjectDataSource()
+ private readonly dataSource: DataSource,
+ ) { }
- private logger = new CustomLogger(RelatorioSinteticoRepository.name, { timestamp: true });
-
- private getQuery(args:IFindPublicacaoRelatorio){
- if(args.aPagar === true){
+ private logger = new CustomLogger(RelatorioSinteticoRepository.name, { timestamp: true });
+
+ private getQuery(args: IFindPublicacaoRelatorio) {
+ if (args.aPagar === true) {
return this.getQueryApagar(args);
- }else if (args.aPagar === false || args.aPagar === undefined){
+ } else if (args.aPagar === false || args.aPagar === undefined) {
return this.getQueryNaoApagar(args);
- }else{
- return this.getQueryApagar(args) + ` union all ` +this.getQueryNaoApagar(args);
+ } else {
+ return this.getQueryApagar(args) + ` union all ` + this.getQueryNaoApagar(args);
}
- }
-
+ }
+
public async findSintetico(args: IFindPublicacaoRelatorio): Promise {
const query = this.getQuery(args);
- this.logger.debug(query);
+ this.logger.debug(compactQuery(query));
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
- let result: any[] = await queryRunner.query(query);
+ let result: any[] = await queryRunner.query(compactQuery(query));
queryRunner.release();
const sinteticos = result.map((r) => new RelatorioSinteticoDto(r));
return sinteticos;
- }
+ }
- public getQueryApagar(args:IFindPublicacaoRelatorio){
- const dataInicio = args.dataInicio.toISOString().slice(0,10)
- const dataFim = args.dataFim.toISOString().slice(0,10)
+ public getQueryApagar(args: IFindPublicacaoRelatorio) {
+ const dataInicio = args.dataInicio.toISOString().slice(0, 10);
+ const dataFim = args.dataFim.toISOString().slice(0, 10);
let query = `WITH subtotal_data AS ( `;
- query = query + ` SELECT `;
- query = query + ` tv."nomeConsorcio", `;
- query = query + ` SUM(tv."valorPago") AS subTotal `;
- query = query + ` FROM transacao_view tv `;
- query = query + ` WHERE tv."valorPago" > 0 `;
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio))) {
- query = query + ` AND tv."datetimeTransacao" BETWEEN '${dataInicio+' 00:00:00'}' and '${dataFim+' 23:59:59'}' `;
- }
- query = query + ` AND tv."itemTransacaoAgrupadoId" IS NULL `
- query = query + ` GROUP BY tv."nomeConsorcio" ), `;
- query = query + `total_data AS ( `;
- query = query + ` SELECT `;
- query = query + ` SUM(tv."valorPago") AS Total `;
- query = query + ` FROM transacao_view tv `;
- query = query + ` WHERE tv."valorPago" > 0 `;
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio))) {
- query = query + ` AND tv."datetimeTransacao" BETWEEN '${dataInicio+' 00:00:00'}' and '${dataFim+' 23:59:59'}' `;
- }
-
- if((args.consorcioNome!==undefined) && !(['Todos'].some(i=>args.consorcioNome?.includes(i)))){
- query = query +` AND tv."nomeConsorcio" in('${args.consorcioNome?.join("','")}')`;
- }else if((['Todos'].some(i=>args.consorcioNome?.includes(i)))
- && (['Todos'].some(i=>args.favorecidoNome?.includes(i))) ||
- ((args.consorcioNome!==undefined) && (args.favorecidoNome!==undefined))){
- query = query +` AND tv."nomeConsorcio" in ('STPC','STPL','VLT','Santa Cruz',
+ query = query + ` SELECT `;
+ query = query + ` tv."nomeConsorcio", `;
+ query = query + ` SUM(tv."valorPago") AS subTotal `;
+ query = query + ` FROM transacao_view tv `;
+ query = query + ` WHERE tv."valorPago" > 0 `;
+ if (dataInicio !== undefined && dataFim !== undefined && (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))) {
+ query = query + ` AND tv."datetimeTransacao" BETWEEN '${dataInicio + ' 00:00:00'}' and '${dataFim + ' 23:59:59'}' `;
+ }
+ query = query + ` AND tv."itemTransacaoAgrupadoId" IS NULL `;
+ query = query + ` GROUP BY tv."nomeConsorcio" ), `;
+ query = query + `total_data AS ( `;
+ query = query + ` SELECT `;
+ query = query + ` SUM(tv."valorPago") AS Total `;
+ query = query + ` FROM transacao_view tv `;
+ query = query + ` WHERE tv."valorPago" > 0 `;
+ if (dataInicio !== undefined && dataFim !== undefined && (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))) {
+ query = query + ` AND tv."datetimeTransacao" BETWEEN '${dataInicio + ' 00:00:00'}' and '${dataFim + ' 23:59:59'}' `;
+ }
+
+ if (args.consorcioNome !== undefined && !['Todos'].some((i) => args.consorcioNome?.includes(i))) {
+ query = query + ` AND tv."nomeConsorcio" in('${args.consorcioNome?.join("','")}')`;
+ } else if ((['Todos'].some((i) => args.consorcioNome?.includes(i)) && ['Todos'].some((i) => args.favorecidoNome?.includes(i))) || (args.consorcioNome !== undefined && args.favorecidoNome !== undefined)) {
+ query =
+ query +
+ ` AND tv."nomeConsorcio" in ('STPC','STPL','VLT','Santa Cruz',
'Internorte','Intersul','Transcarioca','MobiRio','TEC') `;
- }else if((['Todos'].some(i=>args.favorecidoNome?.includes(i)))){
- query = query +` AND tv."nomeConsorcio" in('STPC','STPL') `;
- }
- query = query + ` AND tv."itemTransacaoAgrupadoId" IS NULL) `;
+ } else if (['Todos'].some((i) => args.favorecidoNome?.includes(i))) {
+ query = query + ` AND tv."nomeConsorcio" in('STPC','STPL') `;
+ }
+ query = query + ` AND tv."itemTransacaoAgrupadoId" IS NULL) `;
- query = query +` SELECT DISTINCT `;
- query = query +` res.*, `;
- query = query +` COALESCE(sub.subTotal, 0) AS subTotal,`;
- query = query +` total_data.Total`;
- query = query +` FROM (`;
+ query = query + ` SELECT DISTINCT `;
+ query = query + ` res.*, `;
+ query = query + ` COALESCE(sub.subTotal, 0) AS subTotal,`;
+ query = query + ` total_data.Total`;
+ query = query + ` FROM (`;
- let body = ` SELECT DISTINCT
+ let body = ` SELECT DISTINCT
tv.id,
(tv."datetimeTransacao":: DATE)::VARCHAR AS datatransacao,
CASE
@@ -105,63 +104,72 @@ export class RelatorioSinteticoRepository {
'a pagar' AS status,
'' AS mensagem_status `;
- body = body + `FROM transacao_view tv `;
- body = body + `LEFT JOIN cliente_favorecido cf ON tv."operadoraCpfCnpj" = cf."cpfCnpj" `;
- body = body +`LEFT JOIN public.user uu on uu."cpfCnpj"=tv."operadoraCpfCnpj" `;
- let conditions = `where tv."valorPago" >0 and`;
-
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio)))
- conditions = conditions + ` tv."datetimeTransacao" between '${dataInicio+' 00:00:00'}' and '${dataFim+' 23:59:59'}' `;
+ body = body + `FROM transacao_view tv `;
+ body = body + `LEFT JOIN cliente_favorecido cf ON tv."operadoraCpfCnpj" = cf."cpfCnpj" `;
+ body = body + `LEFT JOIN public.user uu on uu."cpfCnpj"=tv."operadoraCpfCnpj" `;
+ let conditions = `where tv."valorPago" >0 and`;
+
+ if (dataInicio !== undefined && dataFim !== undefined && (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))) conditions = conditions + ` tv."datetimeTransacao" between '${dataInicio + ' 00:00:00'}' and '${dataFim + ' 23:59:59'}' `;
- if((args.consorcioNome!==undefined) && !(['Todos'].some(i=>args.consorcioNome?.includes(i)))){
- conditions = conditions +` and tv."nomeConsorcio" in('${args.consorcioNome?.join("','")}')`;
- }else if((args.favorecidoNome!==undefined) && !(['Todos'].some(i=>args.favorecidoNome?.includes(i)))){
- conditions = conditions +` and cf."nome" in('${args.favorecidoNome?.join("','")}')`;
- }else if((['Todos'].some(i=>args.consorcioNome?.includes(i))) && (['Todos'].some(i=>args.favorecidoNome?.includes(i)))
- || ((args.consorcioNome!==undefined) && (args.favorecidoNome!==undefined))){
- conditions = conditions +` and tv."nomeConsorcio"
+ if (args.consorcioNome !== undefined && !['Todos'].some((i) => args.consorcioNome?.includes(i))) {
+ conditions = conditions + ` and tv."nomeConsorcio" in('${args.consorcioNome?.join("','")}')`;
+ } else if (args.favorecidoNome !== undefined && !['Todos'].some((i) => args.favorecidoNome?.includes(i))) {
+ conditions = conditions + ` and cf."nome" in('${args.favorecidoNome?.join("','")}')`;
+ } else if ((['Todos'].some((i) => args.consorcioNome?.includes(i)) && ['Todos'].some((i) => args.favorecidoNome?.includes(i))) || (args.consorcioNome !== undefined && args.favorecidoNome !== undefined)) {
+ conditions =
+ conditions +
+ ` and tv."nomeConsorcio"
in ('STPC','STPL','VLT','Santa Cruz','Internorte','Intersul','Transcarioca','MobiRio','TEC') `;
- }else if((['Todos'].some(i=>args.favorecidoNome?.includes(i))) && (args.consorcioNome!==undefined)){
- conditions = conditions +` and tv."nomeConsorcio" in('STPC','STPL',${args.consorcioNome?.join("','")}) `;
- }else if((['Todos'].some(i=>args.favorecidoNome?.includes(i)))){
- conditions = conditions +` and tv."nomeConsorcio" in('STPC','STPL') `;
- }
+ } else if (['Todos'].some((i) => args.favorecidoNome?.includes(i)) && args.consorcioNome !== undefined) {
+ conditions = conditions + ` and tv."nomeConsorcio" in('STPC','STPL',${args.consorcioNome?.join("','")}) `;
+ } else if (['Todos'].some((i) => args.favorecidoNome?.includes(i))) {
+ conditions = conditions + ` and tv."nomeConsorcio" in('STPC','STPL') `;
+ }
- let footer = `) AS res
+ let footer = `) AS res
LEFT JOIN subtotal_data sub
ON res."consorcio" = sub."nomeConsorcio"
CROSS JOIN total_data
- ORDER BY res."consorcio", res."favorecido", res."datapagamento"`
+ ORDER BY res."consorcio", res."favorecido", res."datapagamento"`;
- let result = ` select * from ( `+ query + body + conditions + footer +` ) as tt where (1=1)`;
+ let result = ` select * from ( ` + query + body + conditions + footer + ` ) as tt where (1=1)`;
return result;
}
- public getQueryNaoApagar(args:IFindPublicacaoRelatorio){
- const dataInicio = args.dataInicio.toISOString().slice(0,10)
- const dataFim = args.dataFim.toISOString().slice(0,10)
+ public getQueryNaoApagar(args: IFindPublicacaoRelatorio) {
+ const dataInicio = args.dataInicio.toISOString().slice(0, 10);
+ const dataFim = args.dataFim.toISOString().slice(0, 10);
+ let valor = '';
let query = ` select distinct res.*, `;
- query = query + `(select sum(ss."valorLancamento")::float from `;
- query = query + ` (select distinct dta.id,dta."valorLancamento"
+ query = query + `(select sum(ss."valorLancamento")::float from `;
+ query =
+ query +
+ ` (select distinct dta.id,dta."valorLancamento"
from detalhe_a dta
inner join item_transacao_agrupado tt on dta."itemTransacaoAgrupadoId"=tt.id
inner join transacao_agrupado tta on tta."id"=tt."transacaoAgrupadoId" and tta."statusId"<>'5'
left join item_transacao itt on itt."itemTransacaoAgrupadoId" = tt."id"
left join arquivo_publicacao app on app."itemTransacaoId"=itt.id
WHERE `;
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio)))
- query = query + ` dta."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
- if(args.emProcessamento!==undefined && args.emProcessamento===true ){
- query = query +` and app."isPago"=false and TRIM(dta."ocorrenciasCnab")='' `
- }else if(args.pago !==undefined){
- query = query +` and app."isPago"=${args.pago} and TRIM(dta."ocorrenciasCnab")<>'' `;
- }
- query = query + ` and tt."nomeConsorcio"=res.consorcio `;
- query = query + ` )as ss) as subTotal, `;
+ if (dataInicio !== undefined && dataFim !== undefined && (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))) query = query + ` dta."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
+ if (args.emProcessamento !== undefined && args.emProcessamento === true) {
+ query = query + ` and app."isPago"=false and TRIM(dta."ocorrenciasCnab")='' `;
+ } else if (args.pago !== undefined) {
+ query = query + ` and app."isPago"=${args.pago} and TRIM(dta."ocorrenciasCnab")<>'' `;
+ }
+ if (args.eleicao !== undefined) {
+
+ query += ` and tt."idOrdemPagamento" LIKE '%U%'`;
+ } else {
+
+ query += `and tt."idOrdemPagamento" NOT LIKE '%U%' `;
+ }
+ query = query + ` and tt."nomeConsorcio"=res.consorcio `;
+ query = query + ` )as ss) as subTotal, `;
- query = query + `(select sum(tt."valorLancamento")::float from
+ query =
+ query +
+ `(select sum(tt."valorLancamento")::float from
(select distinct dta.id,dta."valorLancamento"
from detalhe_a dta
inner join item_transacao_agrupado tt on dta."itemTransacaoAgrupadoId"=tt.id
@@ -169,34 +177,42 @@ export class RelatorioSinteticoRepository {
left join item_transacao itt on itt."itemTransacaoAgrupadoId" = tt."id"
left join arquivo_publicacao app on app."itemTransacaoId"=itt.id
WHERE `;
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio)))
- query = query + ` dta."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
- if(args.emProcessamento!==undefined && args.emProcessamento===true ){
- query = query +` and app."isPago"=false and TRIM(dta."ocorrenciasCnab")='' `
- }else if(args.pago !==undefined){
- query = query +` and app."isPago"=${args.pago} and TRIM(dta."ocorrenciasCnab")<>'' `;
- }
+ if (dataInicio !== undefined && dataFim !== undefined && (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))) query = query + ` dta."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
+ if (args.emProcessamento !== undefined && args.emProcessamento === true) {
+ query = query + ` and app."isPago"=false and TRIM(dta."ocorrenciasCnab")='' `;
+ } else if (args.pago !== undefined) {
+ query = query + ` and app."isPago"=${args.pago} and TRIM(dta."ocorrenciasCnab")<>'' `;
+ }
- if((args.consorcioNome!==undefined) && !(['Todos'].some(i=>args.consorcioNome?.includes(i)))){
- query = query +` and tt."nomeConsorcio" in('${args.consorcioNome?.join("','")}')`;
- }else
- if((args.favorecidoNome!==undefined) && !(['Todos'].some(i=>args.favorecidoNome?.includes(i))))
- query = query +` and tt."nomeConsorcio" in(res.consorcio) `;
- else if((['Todos'].some(i=>args.consorcioNome?.includes(i)))
- && (['Todos'].some(i=>args.favorecidoNome?.includes(i))) ||
- ((args.consorcioNome!==undefined) && (args.favorecidoNome!==undefined))){
- query = query +` and tt."nomeConsorcio" in ('STPC','STPL','VLT','Santa Cruz',
+ if (args.consorcioNome !== undefined && !['Todos'].some((i) => args.consorcioNome?.includes(i))) {
+ query = query + ` and tt."nomeConsorcio" in('${args.consorcioNome?.join("','")}')`;
+ } else if (args.favorecidoNome !== undefined && !['Todos'].some((i) => args.favorecidoNome?.includes(i))) query = query + ` and tt."nomeConsorcio" in(res.consorcio) `;
+ else if ((['Todos'].some((i) => args.consorcioNome?.includes(i)) && ['Todos'].some((i) => args.favorecidoNome?.includes(i))) || (args.consorcioNome !== undefined && args.favorecidoNome !== undefined)) {
+ query =
+ query +
+ ` and tt."nomeConsorcio" in ('STPC','STPL','VLT','Santa Cruz',
'Internorte','Intersul','Transcarioca','MobiRio','TEC') `;
- }
- else if((['Todos'].some(i=>args.favorecidoNome?.includes(i)))){
- query = query +` and tt."nomeConsorcio" in('STPC','STPL','TEC') `;
- }
- query = query + ` )as tt )as total `;
-
+ } else if (['Todos'].some((i) => args.favorecidoNome?.includes(i))) {
+ query = query + ` and tt."nomeConsorcio" in('STPC','STPL','TEC') `;
+ }
+ if (args.eleicao !== undefined) {
+ if(args.pago){
+ valor = 'da."valorRealEfetivado"'
+ } else {
+ valor = 'it."valor"'
+ }
+ query += ` and tt."idOrdemPagamento" LIKE '%U%'`;
+ } else {
+ valor = 'it."valor"'
+ query += `and tt."idOrdemPagamento" NOT LIKE '%U%' `;
+ }
+ query = query + ` )as tt )as total `;
+
query = query + `from ( `;
- query = query +`
+ query =
+ query +
+ `
select distinct
it.id,
case
@@ -215,16 +231,18 @@ export class RelatorioSinteticoRepository {
it."nomeConsorcio" AS consorcio,
cf.nome AS favorecido,
cf."cpfCnpj",
- it."valor"::float as valor,
+ ${valor}::float as valor,
case
when(not(ap."isPago") and TRIM(da."ocorrenciasCnab")='')then 'Aguardando Pagamento'
when (ap."isPago") then 'pago'
when (not (ap."isPago")) then 'naopago'
else 'apagar' end AS status,
case when (not (ap."isPago")) then oc."message"
- else '' end As mensagem_status `;
-
- query = query + ` from item_transacao_agrupado ita
+ else '' end As mensagem_status `;
+
+ query =
+ query +
+ ` from item_transacao_agrupado ita
inner join detalhe_a da on da."itemTransacaoAgrupadoId"= ita.id
inner join item_transacao it on ita.id = it."itemTransacaoAgrupadoId"
inner join transacao_agrupado ta on ta."id"=ita."transacaoAgrupadoId" and ta."statusId"<>'5'
@@ -232,39 +250,70 @@ export class RelatorioSinteticoRepository {
inner join cliente_favorecido cf on cf.id=it."clienteFavorecidoId"
left join ocorrencia oc on oc."detalheAId"=da.id
where `;
- if(dataInicio!==undefined && dataFim!==undefined &&
- (dataFim === dataInicio || new Date(dataFim)>new Date(dataInicio)))
- query = query + ` da."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
+ if (dataInicio !== undefined && dataFim !== undefined && (dataFim === dataInicio || new Date(dataFim) > new Date(dataInicio))) query = query + ` da."dataVencimento" between '${dataInicio}' and '${dataFim}'`;
+ if (args.eleicao !== undefined) {
+
+ query += ` and ita."idOrdemPagamento" LIKE '%U%'`;
+ } else {
- if((args.consorcioNome!==undefined) && !(['Todos'].some(i=>args.consorcioNome?.includes(i)))){
- query = query +` and it."nomeConsorcio" in('${args.consorcioNome?.join("','")}')`;
- }else if((args.favorecidoNome!==undefined) && !(['Todos'].some(i=>args.favorecidoNome?.includes(i)))){
- query = query +` and cf."nome" in('${args.favorecidoNome?.join("','")}')`;
- }else if( (['Todos'].some(i=>args.consorcioNome?.includes(i))) && (['Todos'].some(i=>args.favorecidoNome?.includes(i)))
- || ((args.consorcioNome!==undefined) && (args.favorecidoNome!==undefined))){
- query = query +` and it."nomeConsorcio"
+ query += `and ita."idOrdemPagamento" NOT LIKE '%U%' `;
+ }
+ if (args.consorcioNome !== undefined && !['Todos'].some((i) => args.consorcioNome?.includes(i))) {
+ query = query + ` and it."nomeConsorcio" in('${args.consorcioNome?.join("','")}')`;
+ } else if (args.favorecidoNome !== undefined && !['Todos'].some((i) => args.favorecidoNome?.includes(i))) {
+ query = query + ` and cf."nome" in('${args.favorecidoNome?.join("','")}')`;
+ } else if ((['Todos'].some((i) => args.consorcioNome?.includes(i)) && ['Todos'].some((i) => args.favorecidoNome?.includes(i))) || (args.consorcioNome !== undefined && args.favorecidoNome !== undefined)) {
+ query =
+ query +
+ ` and it."nomeConsorcio"
in ('STPC','STPL','VLT','Santa Cruz','Internorte','Intersul','Transcarioca','MobiRio','TEC') `;
- }else if((['Todos'].some(i=>args.favorecidoNome?.includes(i)))){
- query = query +` and it."nomeConsorcio" in('STPC','STPL','TEC') `;
+ } else if (['Todos'].some((i) => args.favorecidoNome?.includes(i))) {
+ query = query + ` and it."nomeConsorcio" in('STPC','STPL','TEC') `;
+ }
+ if (args.emProcessamento === true) {
+ if (args.pago === true) {
+ query += `
+ and (
+ ap."isPago" = true
+ OR (ap."isPago" = false AND TRIM(da."ocorrenciasCnab") = '')
+ )
+ `
+ } else if (args.pago === false) {
+ query += `
+ and (
+ ap."isPago" = false
+ AND (
+ TRIM(da."ocorrenciasCnab") = ''
+ OR TRIM(da."ocorrenciasCnab") <> ''
+ )
+ )
+ `
+ } else {
+ query += `
+ and ap."isPago" = false
+ AND TRIM(da."ocorrenciasCnab") = ''
+ `
+ }
+ } else if (args.pago !== undefined) {
+ query += `
+ and ap."isPago" = ${args.pago}
+ AND TRIM(da."ocorrenciasCnab") <> ''
+ `
}
- if(args.emProcessamento!==undefined && args.emProcessamento===true){
- query = query +` and ap."isPago"=false and TRIM(da."ocorrenciasCnab")='' `
- }else if(args.pago !==undefined){
- query = query + ` and ap."isPago"=${args.pago} and TRIM(da."ocorrenciasCnab")<>'' `;
- }
-
- if(args.valorMin!==undefined)
- query = query +` and it."valor">=${args.valorMin}`;
+
+
+ if (args.valorMin !== undefined) query = query + ` and it."valor">=${args.valorMin}`;
+
+ if (args.valorMax !== undefined) query = query + ` and it."valor"<=${args.valorMax}`;
+
+ query =
+ query +
+ ` ) as res
+ order by "consorcio", "favorecido","datapagamento" `;
+
+ this.logger.debug(compactQuery(query));
- if(args.valorMax!==undefined)
- query = query + ` and it."valor"<=${args.valorMax}`;
-
- query = query + ` ) as res
- order by "consorcio", "favorecido","datapagamento" `;
-
- this.logger.debug(query);
-
return query;
}
-}
\ No newline at end of file
+}
diff --git a/src/relatorio/relatorio.controller.ts b/src/relatorio/relatorio.controller.ts
index 20ef30871..c104ac775 100644
--- a/src/relatorio/relatorio.controller.ts
+++ b/src/relatorio/relatorio.controller.ts
@@ -13,7 +13,7 @@ import { RelatorioService } from './relatorio.service';
version: '1',
})
export class RelatorioController {
- constructor(private relatorioService: RelatorioService) {}
+ constructor(private relatorioService: RelatorioService) { }
@ApiQuery({ name: 'dataInicio', description: 'Data da Ordem de Pagamento Inicial', required: true, type: String })
@ApiQuery({ name: 'dataFim', description: 'Data da Ordem de Pagamento Final', required: true, type: String })
@@ -24,6 +24,7 @@ export class RelatorioController {
@ApiQuery({ name: 'pago', required: false, type: Boolean, description: ApiDescription({ _: 'Se o pagamento foi pago com sucesso.', default: false }) })
@ApiQuery({ name: 'aPagar', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for a pagar', default: false }) })
@ApiQuery({ name: 'emProcessamento', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for a emProcessamento', default: false }) })
+ @ApiQuery({ name: 'eleicao', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for em Processamento', default: false }) })
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'))
@Get('consolidado')
@@ -33,25 +34,27 @@ export class RelatorioController {
@Query('dataFim', new ParseDatePipe({ dateOnly: true }))
dataFim: Date,
@Query('favorecidoNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
- favorecidoNome: string[],
+ favorecidoNome: string[],
@Query('consorcioNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
- consorcioNome: string[],
+ consorcioNome: string[],
@Query('valorMin', new ParseNumberPipe({ optional: true }))
valorMin: number | undefined,
@Query('valorMax', new ParseNumberPipe({ optional: true }))
valorMax: number | undefined,
- @Query('pago',new ParseBooleanPipe({ optional: true })) pago: boolean | undefined,
- @Query('aPagar',new ParseBooleanPipe({ optional: true })) aPagar: boolean | undefined,
- @Query('emProcessamento',new ParseBooleanPipe({ optional: true })) emProcessamento: boolean | undefined
- ){
- try{
- const result = await this.relatorioService.findConsolidado({
- dataInicio,dataFim, favorecidoNome, consorcioNome, valorMin, valorMax, pago, aPagar,emProcessamento
- });
- return result;
- }catch(e){
- return new HttpException({ error: e.message}, HttpStatus.BAD_REQUEST);
- }
+ @Query('pago', new ParseBooleanPipe({ optional: true })) pago: boolean | undefined,
+ @Query('aPagar', new ParseBooleanPipe({ optional: true })) aPagar: boolean | undefined,
+ @Query('emProcessamento', new ParseBooleanPipe({ optional: true })) emProcessamento: boolean | undefined,
+ @Query('eleicao', new ParseBooleanPipe({ optional: true })) eleicao: boolean | undefined
+
+ ) {
+ try {
+ const result = await this.relatorioService.findConsolidado({
+ dataInicio, dataFim, favorecidoNome, consorcioNome, valorMin, valorMax, pago, aPagar, emProcessamento, eleicao
+ });
+ return result;
+ } catch (e) {
+ return new HttpException({ error: e.message }, HttpStatus.BAD_REQUEST);
+ }
}
@ApiQuery({ name: 'dataInicio', description: 'Data da Ordem de Pagamento Inicial', required: true, type: String })
@@ -63,6 +66,7 @@ export class RelatorioController {
@ApiQuery({ name: 'pago', required: false, type: Boolean, description: ApiDescription({ _: 'Se o pagamento foi pago com sucesso.', default: false }) })
@ApiQuery({ name: 'aPagar', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for a pagar', default: false }) })
@ApiQuery({ name: 'emProcessamento', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for em Processamento', default: false }) })
+ @ApiQuery({ name: 'eleicao', required: false, type: Boolean, description: ApiDescription({ _: 'Se o status for em Processamento', default: false }) })
@HttpCode(HttpStatus.OK)
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'))
@@ -73,25 +77,27 @@ export class RelatorioController {
@Query('dataFim', new ParseDatePipe({ dateOnly: true }))
dataFim: Date,
@Query('favorecidoNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
- favorecidoNome: string[],
+ favorecidoNome: string[],
@Query('consorcioNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
- consorcioNome: string[],
+ consorcioNome: string[],
@Query('valorMin', new ParseNumberPipe({ optional: true }))
valorMin: number | undefined,
@Query('valorMax', new ParseNumberPipe({ optional: true }))
valorMax: number | undefined,
- @Query('pago',new ParseBooleanPipe({ optional: true })) pago: boolean | undefined,
- @Query('aPagar',new ParseBooleanPipe({ optional: true })) aPagar: boolean | undefined,
- @Query('emProcessamento',new ParseBooleanPipe({ optional: true })) emProcessamento: boolean | undefined
+ @Query('pago', new ParseBooleanPipe({ optional: true })) pago: boolean | undefined,
+ @Query('aPagar', new ParseBooleanPipe({ optional: true })) aPagar: boolean | undefined,
+ @Query('emProcessamento', new ParseBooleanPipe({ optional: true })) emProcessamento: boolean | undefined,
+ @Query('eleicao', new ParseBooleanPipe({ optional: true })) eleicao: boolean | undefined,
+
) {
- try{
+ try {
const result = await this.relatorioService.findSintetico({
- dataInicio,dataFim, favorecidoNome, consorcioNome, valorMin, valorMax, pago, aPagar,emProcessamento
+ dataInicio, dataFim, favorecidoNome, consorcioNome, valorMin, valorMax, pago, aPagar, emProcessamento, eleicao
});
return result;
- }catch(e){
- return new HttpException({ error: e.message}, HttpStatus.BAD_REQUEST);
- }
+ } catch (e) {
+ return new HttpException({ error: e.message }, HttpStatus.BAD_REQUEST);
+ }
}
@ApiQuery({ name: 'dataInicio', description: 'Data da Ordem de Pagamento Inicial', required: true, type: String })
@@ -112,24 +118,24 @@ export class RelatorioController {
@Query('dataFim', new ParseDatePipe({ dateOnly: true }))
dataFim: Date,
@Query('favorecidoNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
- favorecidoNome: string[],
+ favorecidoNome: string[],
@Query('consorcioNome', new ParseArrayPipe({ items: String, separator: ',', optional: true }))
- consorcioNome: string[],
+ consorcioNome: string[],
@Query('valorMin', new ParseNumberPipe({ optional: true }))
valorMin: number | undefined,
@Query('valorMax', new ParseNumberPipe({ optional: true }))
valorMax: number | undefined,
- @Query('pago',new ParseBooleanPipe({ optional: true })) pago: boolean | undefined,
- @Query('aPagar',new ParseBooleanPipe({ optional: true })) aPagar: boolean | undefined
+ @Query('pago', new ParseBooleanPipe({ optional: true })) pago: boolean | undefined,
+ @Query('aPagar', new ParseBooleanPipe({ optional: true })) aPagar: boolean | undefined
) {
- try{
+ try {
const result = await this.relatorioService.findAnalitico({
- dataInicio,dataFim, favorecidoNome, consorcioNome, valorMin, valorMax, pago, aPagar
+ dataInicio, dataFim, favorecidoNome, consorcioNome, valorMin, valorMax, pago, aPagar
});
return result;
- }catch(e){
- return new HttpException({ error: e.message}, HttpStatus.BAD_REQUEST);
- }
+ } catch (e) {
+ return new HttpException({ error: e.message }, HttpStatus.BAD_REQUEST);
+ }
}
}
\ No newline at end of file
diff --git a/src/relatorio/relatorio.module.ts b/src/relatorio/relatorio.module.ts
index fd1c9f320..7f93ccb5a 100644
--- a/src/relatorio/relatorio.module.ts
+++ b/src/relatorio/relatorio.module.ts
@@ -5,12 +5,15 @@ import { CnabModule } from 'src/cnab/cnab.module';
import { RelatorioConsolidadoRepository } from './relatorio-consolidado.repository';
import { RelatorioSinteticoRepository } from './relatorio-sintetico.repository';
import { RelatorioAnaliticoRepository } from './relatorio-analitico.repository';
+import { RelatorioNovoRemessaService } from './relatorio-novo-remessa.service';
+import { RelatorioNovoRemessaController } from './relatorio-novo-remessa.controller';
+import { RelatorioNovoRemessaRepository } from './relatorio-novo-remessa.repository';
@Module({
imports:[CnabModule],
- controllers: [RelatorioController],
- providers: [RelatorioService, RelatorioConsolidadoRepository,
- RelatorioSinteticoRepository,RelatorioAnaliticoRepository]
+ controllers: [RelatorioController, RelatorioNovoRemessaController],
+ providers: [RelatorioService, RelatorioNovoRemessaService, RelatorioConsolidadoRepository,
+ RelatorioSinteticoRepository,RelatorioAnaliticoRepository, RelatorioNovoRemessaRepository]
})
export class RelatorioModule {}
diff --git a/src/sftp/sftp.service.ts b/src/sftp/sftp.service.ts
index 98846530f..78010fc56 100644
--- a/src/sftp/sftp.service.ts
+++ b/src/sftp/sftp.service.ts
@@ -125,9 +125,9 @@ export class SftpService implements OnModuleInit, OnModuleLoad {
public async submitCnabRemessa(content: string): Promise {
const METHOD = 'submitCnabRemessa';
await this.connectClient();
- const remotePath = this.dir(`${this.FOLDERS.REMESSA}/${this.generateRemessaName()}`);
+ const remotePath = this.dir(`${this.FOLDERS.BACKUP_REMESSA}/${this.generateRemessaName()}`);
await this.sftpClient.upload(Buffer.from(content, 'utf-8'), remotePath);
- await this.submitCnabBackupRemessa(content);
+ await this.submitCnabBackupRemessa(content);
this.logger.log(`Arquivo CNAB carregado em ${remotePath}`, METHOD);
return remotePath;
}
diff --git a/src/transacao-view/transacao-view.entity.ts b/src/transacao-view/transacao-view.entity.ts
index 57636fbf4..3801654d4 100644
--- a/src/transacao-view/transacao-view.entity.ts
+++ b/src/transacao-view/transacao-view.entity.ts
@@ -26,6 +26,7 @@ export interface ITransacaoView {
itemTransacaoAgrupadoId: number | null;
createdAt: Date;
updatedAt: Date;
+ idOrdemPagamento: number | null;
}
/**
@@ -152,6 +153,7 @@ export class TransacaoView {
itemTransacaoAgrupadoId: `${table ? `${table}.` : ''}"itemTransacaoAgrupadoId"`,
createdAt: `${table ? `${table}.` : ''}"createdAt"`,
updatedAt: `${table ? `${table}.` : ''}"updatedAt"`,
+ idOrdemPagamento: `${table ? `${table}.` : ''}"idOrdemPagamento"`,
};
}
@@ -177,6 +179,7 @@ export class TransacaoView {
itemTransacaoAgrupadoId: 'INT',
createdAt: 'TIMESTAMP',
updatedAt: 'TIMESTAMP',
+ idOrdemPagamento: 'INT',
};
public static fromBigqueryTransacao(bq: BigqueryTransacao) {
diff --git a/src/utils/api-param/common-api-params.ts b/src/utils/api-param/common-api-params.ts
index 02fd57be4..706dce491 100644
--- a/src/utils/api-param/common-api-params.ts
+++ b/src/utils/api-param/common-api-params.ts
@@ -13,4 +13,20 @@ export const CommonApiParams = {
default: 'Your logged user id (me / request.user.id)',
}),
} as ApiParamOptions,
+ ordemPagamentoAgrupadoId: {
+ name: 'ordemPagamentoAgrupadoId',
+ type: Number,
+ required: true,
+ description: ApiDescription({
+ default: 'O ID das ordem pagamento agrupadas',
+ }),
+ },
+ ordemPagamentoId: {
+ name: 'ordemPagamentoId',
+ type: Number,
+ required: true,
+ description: ApiDescription({
+ default: 'O ID da ordem de pagamento',
+ }),
+ }
};
diff --git a/src/utils/interfaces/request.interface.ts b/src/utils/interfaces/request.interface.ts
index cafc93433..f98631d6f 100644
--- a/src/utils/interfaces/request.interface.ts
+++ b/src/utils/interfaces/request.interface.ts
@@ -1,6 +1,9 @@
+import { Role } from '../../roles/entities/role.entity';
+
export interface IRequest {
user: {
- id: number;
+ id: number,
+ role: Role
};
method: string;
protocol: string;
diff --git a/src/utils/request-utils.ts b/src/utils/request-utils.ts
index 27026a5ba..4befebdf5 100644
--- a/src/utils/request-utils.ts
+++ b/src/utils/request-utils.ts
@@ -1,7 +1,23 @@
import { IRequest } from './interfaces/request.interface';
+import { HttpException, HttpStatus } from '@nestjs/common';
export function getRequestLog(request: IRequest) {
return `${request.method} ${request.protocol}://${request.get('Host')}${
request.originalUrl
}`;
}
+
+export function canProceed(request: IRequest, userId: number | undefined) {
+ if (request.user && request.user.role && request.user.role.name) {
+ if (isUser(request) && (request.user.id != userId)) {
+ throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
+ }
+ }
+}
+
+export function isUser(request: IRequest) {
+ if (request.user && request.user.role && request.user.role.name) {
+ return request.user.role.name.toUpperCase().startsWith('USER');
+ }
+ return false;
+}
diff --git a/src/utils/type-utils.ts b/src/utils/type-utils.ts
index 4f494e701..de61de8ec 100644
--- a/src/utils/type-utils.ts
+++ b/src/utils/type-utils.ts
@@ -43,3 +43,12 @@ export function isStringNumber(val: string | null | undefined): val is string {
export function isStringDecimal(val: string | null | undefined): val is string {
return isStringNumber(val) && Number(val) % 1 != 0;
}
+
+export function replaceUndefinedWithNull(obj: Record): void {
+ for (const key in obj) {
+ if (obj[key] === undefined) {
+ obj[key] = null;
+ }
+ }
+}
+