Skip to content

Commit

Permalink
Merge branch 'develop' into feature-job-remessa
Browse files Browse the repository at this point in the history
  • Loading branch information
williamfl2007 committed Feb 28, 2024
2 parents 3050639 + 22557fa commit 010d0fc
Show file tree
Hide file tree
Showing 25 changed files with 533 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/bigquery/bigquery.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class BigqueryService {
* Run bigquery query with complete log and error handling
* @throws `HttpException`
*/
public async runQuery(bqInstance: BQSInstances, query: string) {
public async query(bqInstance: BQSInstances, query: string) {
this.logger.debug('Query fetch started');
console.log('bigquery:', query);
try {
Expand Down
30 changes: 30 additions & 0 deletions src/bigquery/entities/transacao.bq-entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export class BqTransacao {
id: number;
data: Date;
hora: number;
datetime_transacao: Date;
datetime_processamento: Date;
datetime_captura: Date;
modo: string;
id_consorcio: string;
consorcio: string;
id_operadora: string;
operadora: string;
servico: string;
sentido: string;
id_veiculo: number;
id_cliente: string;
id_transacao: string;
tipo_pagamento: string;
tipo_transacao: string;
tipo_gratuidade: string;
tipo_integracao: string;
id_integracao: number;
latitude: number;
longitude: number;
stop_id: number;
stop_lat: number;
stop_lon: number;
valor_transacao: number;
versao: string;
}
9 changes: 9 additions & 0 deletions src/bigquery/interfaces/bq-find-transacao-by.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface IBqFetchTransacao {
cpfCnpj?: string;
startDate?: Date;
endDate?: Date;
limit?: number;
offset?: number;
getToday?: boolean;
previousDays?: boolean;
}
10 changes: 10 additions & 0 deletions src/bigquery/maps/bq-transacao-tipo-integracao.map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Ticket revenues integration type map
*/
export const BqTsansacaoTipoIntegracaoMap = {
3: 'Bu municipal',
2: 'Integração',
1: 'Transferência',
0: 'Sem integração',
4: 'Bu intermunicipal',
};
8 changes: 8 additions & 0 deletions src/bigquery/maps/bq-transacao-tipo-pagamento.map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Ticket revenues payment type map
*/
export const BqTransacaoTipoPagamentoMap = {
1: 'Cartão',
2: 'QRCode',
3: 'NFC',
};
25 changes: 25 additions & 0 deletions src/bigquery/maps/bq-transacao-tipo-transacao.map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Ticket revenues transaction type map
*
* Business rules:
* - "Integral" = Débito + Botoeria (both are considered "Integral" type).
* See {@link https://github.com/RJ-SMTR/api-cct/issues/177#issuecomment-1934531824 Issue #177, item 1 - GitHub}
*
* Matching id or literal values.
* See {@link https://github.com/RJ-SMTR/api-cct/issues/168#issuecomment-1900546567 Issue #168 - GitHub}
*/
export const BqTransacaoTipoTransacaoMap = {
/** Originally 1 = Débito */
1: 'Integral',
2: 'Recarga',
98: 'Riocard',
6: 'Bloqueio',
/** Originally 99 = Botoeria */
99: 'Integral',
21: 'Gratuidade',
3: 'Cancelamento',
4: 'Integração',
Débito: 'Integral',
/** Botoeria = payment in cash */
Botoeria: 'Integral',
};
228 changes: 228 additions & 0 deletions src/bigquery/repositories/bq-transacao.repository.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
import { Injectable, Logger } from '@nestjs/common';
import { BQSInstances, BigqueryService } from '../bigquery.service';
import { BqTransacao } from '../entities/transacao.bq-entity';
import { IBqFetchTransacao } from '../interfaces/bq-find-transacao-by.interface';
import { SettingsService } from 'src/settings/settings.service';
import { appSettings } from 'src/settings/app.settings';
import { BigqueryEnvironment } from 'src/settings/enums/bigquery-env.enum';
import { QueryBuilder } from 'src/utils/query-builder/query-builder';
import { isCpfOrCnpj } from 'src/utils/cpf-cnpj';
import { TRIntegrationTypeMap } from 'src/ticket-revenues/maps/ticket-revenues.map';
import { BqTsansacaoTipoIntegracaoMap } from '../maps/bq-transacao-tipo-integracao.map';
import { BqTransacaoTipoTransacaoMap } from '../maps/bq-transacao-tipo-transacao.map';
import { BqTransacaoTipoPagamentoMap } from '../maps/bq-transacao-tipo-pagamento.map';

@Injectable()
export class BqTransacaoRepositoryService {
private logger: Logger = new Logger('BqTransacaoRepositoryService', {
timestamp: true,
});

constructor(
private readonly bigqueryService: BigqueryService,
private readonly settingsService: SettingsService,
) {}

public async findTransacaoBy(
filter?: IBqFetchTransacao,
): Promise<BqTransacao[]> {
const transacoes: BqTransacao[] = (await this.fetchTransacao(filter)).data;
return transacoes;
}

private async fetchTransacao(
args?: IBqFetchTransacao,
): Promise<{ data: BqTransacao[]; countAll: number }> {
const qArgs = await this.getQueryArgs(args);
const query =
`
SELECT
CAST(t.data AS STRING) AS partitionDate,
t.hora AS processingHour,
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 AS modo,
t.servico AS servico,
t.sentido AS sentido,
t.id_veiculo AS id_veiculo,
t.id_cliente AS id_cliente,
t.id_transacao AS id_transacao,
t.${qArgs.tTipoPgto} AS tipo_pagamento,
t.tipo_transacao AS tipo_transacao,
t.id_tipo_integracao AS id_tipo_integracao,
t.id_integracao AS id_integracao,
t.latitude AS latitude,
t.longitude AS longitude,
t.stop_id AS stop_id,
t.stop_lat AS stop_lat,
t.stop_lon AS stop_lon,
CASE WHEN t.tipo_transacao = 'Integração' THEN i.valor_transacao_total ELSE t.valor_transacao END AS valor_transacao,
t.versao AS bqDataVersion,
(${qArgs.countQuery}) AS count,
'ok' AS status
FROM \`${qArgs.transacao}\` t\n` +
qArgs.joinCpfCnpj +
'\n' +
qArgs.joinIntegracao +
'\n' +
(qArgs.qWhere.length ? `WHERE ${qArgs.qWhere}\n` : '') +
`UNION ALL
SELECT ${'null, '.repeat(22)}
(${qArgs.countQuery}) AS count, 'empty' AS status` +
`\nORDER BY t.data DESC, t.hora DESC` +
(qArgs?.limit !== undefined ? `\nLIMIT ${qArgs.limit + 1}` : '') +
(qArgs?.offset !== undefined ? `\nOFFSET ${qArgs.offset}` : '');
const queryResult = await this.bigqueryService.query(
BQSInstances.smtr,
query,
);

const count: number = queryResult[0].count;
// Remove unwanted keys and remove last item (all null if empty)
let transacoes: BqTransacao[] = queryResult.map((i) => {
delete i.status;
delete i.count;
return i;
});
transacoes.pop();
transacoes = this.mapBqTransacao(transacoes);

return {
data: transacoes,
countAll: count,
};
}

private async getQueryArgs(args?: IBqFetchTransacao): Promise<{
qWhere: string;
bucket: string;
transacao: string;
integracao: string;
tTipoPgto: string;
joinCpfCnpj: string;
joinIntegracao: string;
countQuery: string;
offset?: number;
limit?: number;
}> {
const IS_BQ_PROD =
(
await this.settingsService.getOneBySettingData(
appSettings.any__bigquery_env,
true,
)
).getValueAsString() === BigqueryEnvironment.Production;
const Q_CONSTS = {
bucket: IS_BQ_PROD ? 'rj-smtr' : 'rj-smtr-dev',
transacao: IS_BQ_PROD
? 'rj-smtr.br_rj_riodejaneiro_bilhetagem.transacao'
: 'rj-smtr-dev.br_rj_riodejaneiro_bilhetagem_cct.transacao',
integracao: IS_BQ_PROD
? 'rj-smtr.br_rj_riodejaneiro_bilhetagem.integracao'
: 'rj-smtr-dev.br_rj_riodejaneiro_bilhetagem_cct.integracao',
tTipoPgto: IS_BQ_PROD ? 'tipo_pagamento' : 'id_tipo_pagamento',
};
// Args
let offset = args?.offset;
const queryBuilder = new QueryBuilder();
queryBuilder.pushOR([]);
if (args?.offset !== undefined && args.limit === undefined) {
this.logger.warn(
"fetchTicketRevenues(): 'offset' is defined but 'limit' is not." +
" 'offset' will be ignored to prevent query fail",
);
offset = undefined;
}

if (args?.startDate) {
const startDate = args.startDate.toISOString().slice(0, 10);
queryBuilder.pushAND(
`DATE(t.datetime_processamento) >= DATE('${startDate}')`,
);
}
if (args?.endDate) {
const endDate = args.endDate.toISOString().slice(0, 10);
queryBuilder.pushAND(
`DATE(t.datetime_processamento) <= DATE('${endDate}')`,
);
}
if (args?.previousDays === true) {
queryBuilder.pushAND(
'DATE(t.datetime_processamento) > DATE(t.datetime_transacao)',
);
}

queryBuilder.pushOR([]);
if (args?.getToday) {
const nowStr = new Date(Date.now()).toISOString().slice(0, 10);
queryBuilder.pushAND(
`DATE(t.datetime_processamento) = DATE('${nowStr}')`,
);
}

let qWhere = queryBuilder.toSQL();
if (args?.cpfCnpj !== undefined) {
const cpfCnpj = args.cpfCnpj;
qWhere =
isCpfOrCnpj(args?.cpfCnpj) === 'cpf'
? `b.documento = '${cpfCnpj}' AND (${qWhere})`
: `b.cnpj = '${cpfCnpj}' AND (${qWhere})`;
}

// Query
const joinCpfCnpj =
isCpfOrCnpj(args?.cpfCnpj) === 'cpf'
? `LEFT JOIN \`${Q_CONSTS.bucket}.cadastro.operadoras\` b ON b.id_operadora = t.id_operadora `
: `LEFT JOIN \`${Q_CONSTS.bucket}.cadastro.consorcios\` b ON b.id_consorcio = t.id_consorcio `;
const joinIntegracao = `INNER JOIN ${Q_CONSTS.integracao} i ON i.id_transacao = t.id_transacao`;

const countQuery =
'SELECT COUNT(*) AS count ' +
`FROM \`${Q_CONSTS.transacao}\` t\n` +
joinCpfCnpj +
'\n' +
joinIntegracao +
'\n' +
(qWhere.length ? ` WHERE ${qWhere}\n` : '');
return {
qWhere,
bucket: Q_CONSTS.bucket,
transacao: Q_CONSTS.transacao,
integracao: Q_CONSTS.integracao,
tTipoPgto: Q_CONSTS.tTipoPgto,
joinCpfCnpj,
joinIntegracao,
countQuery,
offset,
limit: args?.limit,
};
}

/**
* Convert id or some values into desired string values
*/
private mapBqTransacao(transacoes: BqTransacao[]): BqTransacao[] {
return transacoes.map((item: BqTransacao) => {
const tipo_transacao = item.tipo_transacao;
const tipo_pagamento = item.tipo_pagamento;
const tipo_integracao = item.tipo_integracao;
Object.values(TRIntegrationTypeMap[0]);
return {
...item,
paymentMediaType:
tipo_pagamento !== null
? BqTransacaoTipoPagamentoMap?.[tipo_pagamento] || tipo_pagamento
: tipo_pagamento,
transportIntegrationType:
tipo_integracao !== null
? BqTsansacaoTipoIntegracaoMap?.[tipo_integracao] || tipo_integracao
: tipo_integracao,
transactionType:
tipo_transacao !== null
? BqTransacaoTipoTransacaoMap?.[tipo_transacao] || tipo_transacao
: tipo_transacao,
};
});
}
}
26 changes: 26 additions & 0 deletions src/cnab/entities/detalhe-a.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { EntityHelper } from '../../utils/entity-helper';

@Entity()
export class DetalheA extends EntityHelper {
id_detalhe_a: number;
id_header_lote: number;
lote_servico: string;
id_cliente_favorecido: number;
tipo_finalidade_conta: string;
dt_vencimento: Date;
tipo_moeda: string;
qtde_moeda: number;
valor_lancamento: number;
num_doc_lancamento: string;
qtde_parcelas: number;
indicador_bloqueio: string;
indicador_forma_parcelamento: string;
periodo_vencimento: Date;
num_parcela: number;
data_efetivacao: Date;
valor_real_efetivado: number;
}

function Entity(): (target: typeof DetalheA) => void | typeof DetalheA {
throw new Error('Function not implemented.');
}
13 changes: 13 additions & 0 deletions src/cnab/entities/detalhe-b.entiy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { EntityHelper } from 'src/utils/entity-helper';

@Entity()
export class DetalheB extends EntityHelper {
id_detalhe_b: number;
id_detalhe_a: number;
nsr: string;
data_vencimento: Date;
}

function Entity(): (target: typeof DetalheB) => void | typeof DetalheB {
throw new Error('Function not implemented.');
}
26 changes: 26 additions & 0 deletions src/cnab/entities/header-arquivo.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { EntityHelper } from 'src/utils/entity-helper';

@Entity()
export class HeaderArquivo extends EntityHelper {
id_header_arquivo: number;
tipo_arquivo: string;
cod_banco: string;
tipo_inscricao: string;
num_inscricao: string;
cod_convenio: string;
param_transmissao: string;
agencia: string;
dv_agencia: string;
num_conta: string;
dv_conta: string;
nome_empresa: string;
dt_geracao: Date;
hr_geracao: Date;
id_transacao: number;
}

function Entity(): (
target: typeof HeaderArquivo,
) => void | typeof HeaderArquivo {
throw new Error('Function not implemented.');
}
Loading

0 comments on commit 010d0fc

Please sign in to comment.