Skip to content

Instantly share code, notes, and snippets.

@alyleite
Last active May 23, 2024 13:27
Show Gist options
  • Save alyleite/d386510bed1de38776a5d59b0a8abe36 to your computer and use it in GitHub Desktop.
Save alyleite/d386510bed1de38776a5d59b0a8abe36 to your computer and use it in GitHub Desktop.
Poc para calcular saldo diário do extrato em c# e ruby
require 'logger'
module AccountManager
class ExtractGenerator < M2yService
attr_reader :alias_account, :alias_branch, :start_date, :end_date, :page, :pages
def initialize(alias_account, alias_branch, start_date, end_date, page)
@alias_account = alias_account
@alias_branch = alias_branch
@start_date = start_date
@end_date = end_date
@page = page
@logger = Logger.new(STDOUT)
@logger.level = Logger::WARN
end
def call
balance = fetch_balance
return if balance[:content].empty?
transactions = fetch_transactions
#return if transactions[:content].empty?
initial_balance = balance[:content][0]['vlSdoIni']
final_balance = balance[:content][0]['vlSdoFin']
@logger.warn("dataInicial: #{start_date}")
@logger.warn("dataFinal: #{end_date}")
@logger.warn("valorInicial: #{initial_balance}")
@logger.warn("valorFinal: #{final_balance}\n")
ordered_transactions = transactions[:content].sort_by { |t| t['dtLanc'] }
grouped_transactions = group_transactions_by_date(ordered_transactions)
all_transactions = [] # List to store all transactions including initial balance
date = Date.strptime(start_date, "%d/%m/%Y") - 1.day
# Add initial balance transaction
add_balance_transaction(all_transactions, date.strftime('%Y%m%d'), initial_balance, true)
current_balance = initial_balance
grouped_transactions.each do |date_t, transactions_t|
@logger.warn("Data: #{date_t}\n")
@logger.warn("Saldo Anterior: #{current_balance}\n")
# Add actual transactions
all_transactions.concat(transactions_t.map(&:to_hash))
calculated_balance = calculate_balance(transactions_t)
balance_day = (current_balance + calculated_balance).round(2)
@logger.warn("Soma dos lançamentos do dia: #{calculated_balance}")
@logger.warn("Saldo do dia: (#{current_balance} + #{calculated_balance}): #{balance_day}\n")
add_balance_transaction(all_transactions, date_t, balance_day)
current_balance += calculated_balance
end
total_balance_change = calculate_balance(ordered_transactions)
@logger.warn("Valor final calculado: #{total_balance_change}")
@logger.warn("Valor dos lançamentos somados (#{initial_balance} + #{total_balance_change}): #{(initial_balance + total_balance_change).round(2)}\n")
@logger.warn("Transactions: #{all_transactions}\n")
all_transactions
end
private
def @logger.warn(message)
@logger.warn(message)
end
def fetch_transactions
AccountManager::Extract.call(@alias_account, @alias_branch, @start_date, @end_date, @page)
end
def fetch_balance
AccountManager::ExtractFindBalance.call(@alias_account, @alias_branch, @start_date, @end_date, @page)
end
def group_transactions_by_date(transactions)
transactions.group_by { |transaction| transaction['dtLanc'] }
end
def calculate_balance(transactions)
transactions.sum do |transaction|
if transaction['tpSinal'] == "D"
-transaction['vlLanc']
else
transaction['vlLanc']
end
end
end
def add_balance_transaction(all_transactions, date, balance_day, first = false)
description = first ? "SALDO ANTERIOR" : "SALDO DO DIA"
all_transactions << { dtLanc: date, dtIncsis: date, tpSinal: "S", vlLanc: balance_day, dsLanc: description, dsTransr: description }
end
end
end
public async Task Main()
{
try
{
var dtIni = 20230101;
var dtFin = 20240507;
var lancamentos = await ConsultaLancamento(dtIni, dtFin);
if(lancamentos.Count == 0) return;
var extrato = await ConsultaExtrato(dtIni, dtFin);
var valorInicial = extrato.VlSdoIni;
var valorFinal = extrato.VlSdoFin;
_logger.LogWarning($"dataInicial: {dtIni}");
_logger.LogWarning($"dataFinal: {dtFin}");
_logger.LogWarning($"valorInicial: {valorInicial}");
_logger.LogWarning($"valorFinal: {valorFinal}\n");
var listaOrdenada = lancamentos.OrderBy(l => l.DtLanc).ToList();
var listaAgrupada = listaOrdenada
.GroupBy(x => x.DtLanc)
.ToDictionary(gdc => gdc.Key, gdc => gdc.ToList());
var valorIni = valorInicial;
foreach (var keyValuePair in listaAgrupada)
{
_logger.LogWarning($"Data: {keyValuePair.Key}\n");
_logger.LogWarning($"Valor Inicial da data: {valorIni}\n");
var valorCalc = keyValuePair.Value.Sum(lancamento => lancamento.TpSinal.Equals("D") ? lancamento.VlLanc * -1 : lancamento.VlLanc);
_logger.LogWarning($"Valor calculado da data: {valorCalc}");
_logger.LogWarning($"Valor final da data: ({valorIni} + {valorCalc}): {valorIni + valorCalc}\n");
valorIni += valorCalc;
}
var valor = listaOrdenada.Sum(lancamento => lancamento.TpSinal.Equals("D") ? lancamento.VlLanc * -1 : lancamento.VlLanc);
_logger.LogWarning($"Valor final calculado: {valor}");
_logger.LogWarning($"Valor dos lançamentos somados ({valorInicial} + {valor}): {valorInicial + valor}\n");
}
catch (Exception ex)
{
_logger.LogError($"Erro execução : {ex.Message}");
}
}
private async Task<List<ConsultaLancamento>> ConsultaLancamento(int dtIni, int dtFin)
{
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
WriteIndented = true,
PropertyNameCaseInsensitive = true,
Converters = { new JsonStringEnumConverter(), new CustomDateTimeConverter("yyyy-MM-dd") }
};
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic ***");
httpClient.DefaultRequestHeaders.Add("Accept", "*/*");
httpClient.DefaultRequestHeaders.Add("Cookie", "XSRF-TOKEN=543e6a98-db4d-46ab-998b-0780c1ebf01e");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var url = "<api>/BJ08M01/BJ08M01/BJ08SS0101C/v2/listaLancamentos";
var requestStr = "{\"cdCta\": 144142, \"dtIni\": " + dtIni + ", \"dtFin\": " + dtFin + ", \"nrAgen\": 19, \"nrInst\": 1414, \"nrSeq\": 0}";
var content = new StringContent(requestStr, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(url, content);
if (!response.StatusCode.Equals(HttpStatusCode.OK)) throw new Exception($"Erro ao realizar requisição: {response.StatusCode} - {response.ReasonPhrase}");
var responseContent = await response.Content.ReadAsStringAsync();
var responseDto = JsonSerializer.Deserialize<ResponseLancamento>(responseContent, options);
if(responseDto?.ConsultaLancamento is null)
throw new Exception($"Erro ao converter json de response dos lançamentos: {responseContent}");
return responseDto.ConsultaLancamento;
}
private async Task<Extrato> ConsultaExtrato(int dtIni, int dtFin)
{
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
WriteIndented = true,
PropertyNameCaseInsensitive = true,
Converters = { new JsonStringEnumConverter(), new CustomDateTimeConverter("yyyy-MM-dd") }
};
using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", "Basic ***");
httpClient.DefaultRequestHeaders.Add("Accept", "*/*");
httpClient.DefaultRequestHeaders.Add("Cookie", "XSRF-TOKEN=543e6a98-db4d-46ab-998b-0780c1ebf01e");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var url = "<api>/BJ08M01/BJ08M01/BJ08SS0101C/consultaExtrato";
var requestStr = "{\"cdCta\": 144142, \"dtIni\": " + dtIni + ", \"dtFin\": " + dtFin + ", \"nrAgen\": 19, \"nrInst\": 1414, \"nrSeq\": 0}";
var content = new StringContent(requestStr, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync(url, content);
if (!response.StatusCode.Equals(HttpStatusCode.OK)) throw new Exception($"Erro ao realizar requisição: {response.StatusCode} - {response.ReasonPhrase}");
var responseContent = await response.Content.ReadAsStringAsync();
var responseDto = JsonSerializer.Deserialize<ResponseExtrato>(responseContent, options);
if(responseDto?.Extrato?.FirstOrDefault() == null)
throw new Exception($"Erro ao converter json de response do extrato: {responseContent}");
return responseDto.Extrato!.FirstOrDefault();
}
public class Response
{
public class Extrato
{
public int CdConta { get; set; }
public decimal VlSdoIni { get; set; }
public decimal VlSdoFin { get; set; }
public decimal VlSdoBlq { get; set; }
public decimal VlLimCre { get; set; }
public decimal VlJurAcu { get; set; }
public decimal VlIofAcu { get; set; }
}
public class ResponseExtrato
{
public List<Extrato> Extrato { get; set; }
}
public class ResponseLancamento
{
public List<ConsultaLancamento> ConsultaLancamento { get; set; }
}
public class ConsultaLancamento
{
public int Seq { get; set; }
public int NrInstituicao { get; set; }
public int NrAgencia { get; set; }
public int NrConta { get; set; }
public string NrCpfCnpj { get; set; }
public string NmPessoa { get; set; }
public int DtLanc { get; set; }
public decimal VlLanc { get; set; }
public string DsLanc { get; set; }
public string TpSinal { get; set; }
public string DtIncsis { get; set; }
public string HrIncsis { get; set; }
public string NrAuten { get; set; }
public string NrAutenTransf { get; set; }
public int? IdTrans { get; set; }
public int? CdTrans { get; set; }
public string DsTransr { get; set; }
public string DsTransc { get; set; }
public int? IdMovto { get; set; }
public string NmContraparte { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment