Compare commits
4 Commits
Unificação
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| df6810922b | |||
| 001c467a7c | |||
| 6848039309 | |||
| 495130efe9 |
2
Download Faturas.Tests/divergencias.csv
Normal file
2
Download Faturas.Tests/divergencias.csv
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
FaturaId Distribuidora oldConsumo_P newConsumo_P oldConsumo_FP newConsumo_FP oldDem_Reg_P newDem_Reg_P oldDem_Reg_FP newDem_Reg_FP oldEn_Reativa_Mvarh newEn_Reativa_Mvarh
|
||||||
|
3471939 0 0,46028 0 5,23094 0 8,6592 0 11,4144 0 0,04156
|
||||||
|
@ -1,19 +1,2 @@
|
|||||||
"ID" "Doc" "Status" "Invalidar" "Origin" "Last changed" "Submitted" "JSON" "Name"
|
"ID ""Doc"" ""Status"" ""Invalidar"" ""Origin"" ""Last changed"" ""Submitted"" ""JSON"" ""Name""";;;;;;;;
|
||||||
"3019848" "View | Raw" "SUCCESS" "" "BOT" "2025-11-11 10:31:03" "2025-11-11 10:30:41" "View | Raw" "1114467.pdf"
|
"3471939 ""View | Raw"" ""SUCCESS"" """" ""BOT"" ""2025-11-11 10:31:03"" ""2025-11-11 10:30:41"" ""View | Raw"" ""1114467.pdf""";;;;;;;;
|
||||||
"3001963" "View | Raw" "SUCCESS" "" "BOT" "2025-11-05 07:14:49" "2025-11-05 07:14:33" "View | Raw" "1104802.pdf"
|
|
||||||
"3001801" "View | Raw" "SUCCESS" "" "BOT" "2025-11-05 05:38:54" "2025-11-05 05:38:34" "View | Raw" "1104653.pdf"
|
|
||||||
"3001795" "View | Raw" "SUCCESS" "" "BOT" "2025-11-05 05:36:13" "2025-11-05 05:36:01" "View | Raw" "1104647.pdf"
|
|
||||||
"2993194" "View | Raw" "SUCCESS" "" "BOT" "2025-11-04 06:28:45" "2025-11-04 06:28:29" "View | Raw" "1098537.pdf"
|
|
||||||
"2988061" "View | Raw" "SUCCESS" "" "BOT" "2025-11-03 12:12:41" "2025-11-03 12:12:20" "View | Raw" "1095060.pdf"
|
|
||||||
"2987758" "View | Raw" "SUCCESS" "" "BOT" "2025-11-03 11:07:52" "2025-11-03 11:07:41" "View | Raw" "1094871.pdf"
|
|
||||||
"2987323" "View | Raw" "SUCCESS" "" "BOT" "2025-11-03 09:08:33" "2025-11-03 09:08:18" "View | Raw" "1094470.pdf"
|
|
||||||
"2987073" "View | Raw" "SUCCESS" "" "BOT" "2025-11-03 07:37:46" "2025-11-03 07:37:34" "View | Raw" "1094220.pdf"
|
|
||||||
"2924492" "View | Raw" "SUCCESS" "" "BOT" "2025-10-15 16:15:59" "2025-10-10 08:26:03" "View | Raw" "1062999.pdf"
|
|
||||||
"2906853" "View | Raw" "SUCCESS" "" "BOT" "2025-10-06 12:50:24" "2025-10-06 12:50:08" "View | Raw" "1058041.pdf"
|
|
||||||
"2906573" "View | Raw" "SUCCESS" "" "BOT" "2025-10-06 12:10:55" "2025-10-06 12:10:31" "View | Raw" "1058009.pdf"
|
|
||||||
"2905879" "View | Raw" "SUCCESS" "" "BOT" "2025-10-06 05:58:46" "2025-10-06 05:58:29" "View | Raw" "1057526.pdf"
|
|
||||||
"2903836" "View | Raw" "SUCCESS" "" "BOT" "2025-10-03 11:42:27" "2025-10-03 11:42:09" "View | Raw" "1056838.pdf"
|
|
||||||
"2901834" "View | Raw" "SUCCESS" "" "BOT" "2025-10-02 10:08:21" "2025-10-02 10:08:08" "View | Raw" "1055842.pdf"
|
|
||||||
"2901695" "View | Raw" "SUCCESS" "" "BOT" "2025-10-02 08:56:24" "2025-10-02 08:56:17" "View | Raw" "1055712.pdf"
|
|
||||||
"2901665" "View | Raw" "SUCCESS" "" "BOT" "2025-10-02 08:42:42" "2025-10-02 08:42:34" "View | Raw" "1055684.pdf"
|
|
||||||
"2901319" "View | Raw" "SUCCESS" "" "BOT" "2025-10-02 06:20:25" "2025-10-02 06:20:09" "View | Raw" "1055363.pdf"
|
|
||||||
|
@ -14,13 +14,13 @@ namespace Download_Faturas.Tests
|
|||||||
public async Task CompareFaturaWithFaturaOld_ShouldReportDifferences()
|
public async Task CompareFaturaWithFaturaOld_ShouldReportDifferences()
|
||||||
{
|
{
|
||||||
// Sample fatura IDs to test (replace with real IDs or fetch dynamically)
|
// Sample fatura IDs to test (replace with real IDs or fetch dynamically)
|
||||||
var faturaIds = LoadCsvColumn("../../../../Download Faturas.Tests/4Docs_2025_07.csv", 0);
|
var faturaIds = LoadCsvColumn("../../../../Faturas.Tests/4Docs_2025_07.csv", 0);
|
||||||
var differences = new List<string>
|
var differences = new List<string>
|
||||||
{
|
{
|
||||||
$"FaturaId\tDistribuidora\toldConsumo_P\tnewConsumo_P\toldConsumo_FP\tnewConsumo_FP\toldDem_Reg_P\tnewDem_Reg_P\toldDem_Reg_FP\tnewDem_Reg_FP\toldEn_Reativa_Mvarh\tnewEn_Reativa_Mvarh"
|
$"FaturaId\tDistribuidora\toldConsumo_P\tnewConsumo_P\toldConsumo_FP\tnewConsumo_FP\toldDem_Reg_P\tnewDem_Reg_P\toldDem_Reg_FP\tnewDem_Reg_FP\toldEn_Reativa_Mvarh\tnewEn_Reativa_Mvarh"
|
||||||
};
|
};
|
||||||
|
|
||||||
using (OleDbConnection conn = new (@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=X:/Middle/Informativo Setorial/Modelo Word/BD1_Testes.accdb;Jet OLEDB:Database Password=gds21"))
|
using (OleDbConnection conn = new (@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb;Jet OLEDB:Database Password=gds21"))
|
||||||
{
|
{
|
||||||
// Open the connection to the test database
|
// Open the connection to the test database
|
||||||
conn.Open();
|
conn.Open();
|
||||||
@ -51,7 +51,7 @@ namespace Download_Faturas.Tests
|
|||||||
|
|
||||||
fatura.Processar(conn); // This will also write to DB, but we want to compare without writing
|
fatura.Processar(conn); // This will also write to DB, but we want to compare without writing
|
||||||
var newVal = GetDadosTusd(conn, fatura.CodTusd);
|
var newVal = GetDadosTusd(conn, fatura.CodTusd);
|
||||||
DeleteTusdRecords(conn, fatura.CodTusd); // Delete old TUSD records before processing
|
//DeleteTusdRecords(conn, fatura.CodTusd); // Delete old TUSD records before processing
|
||||||
|
|
||||||
if (!Equals(newVal, oldVal))
|
if (!Equals(newVal, oldVal))
|
||||||
{
|
{
|
||||||
@ -126,7 +126,7 @@ namespace Download_Faturas.Tests
|
|||||||
if (line != null)
|
if (line != null)
|
||||||
{
|
{
|
||||||
var values = line.Split('\t');
|
var values = line.Split('\t');
|
||||||
if (values.Length > columnIndex && (values[4] == "\"API\"" || values[4] == "\"BOT\""))
|
if (values.Length > columnIndex && (values[4] == "\"\"API\"\"" || values[4] == "\"\"BOT\"\""))
|
||||||
{
|
{
|
||||||
columnData.Add(values[columnIndex].Replace("\"", "").Trim());
|
columnData.Add(values[columnIndex].Replace("\"", "").Trim());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,12 +6,18 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||||
<PackageReference Include="xunit" Version="2.9.3" />
|
<PackageReference Include="xunit.v3" Version="3.2.2" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -34,26 +34,6 @@ namespace Faturas
|
|||||||
/// <param name="id">Identificador da fatura no serviço 4docs.</param>
|
/// <param name="id">Identificador da fatura no serviço 4docs.</param>
|
||||||
/// <param name="fatura_path">Caminho completo para o arquivo PDF da fatura local.</param>
|
/// <param name="fatura_path">Caminho completo para o arquivo PDF da fatura local.</param>
|
||||||
/// <param name="httpClient">Instância de HttpClient usada para realizar chamadas HTTP à API.</param>
|
/// <param name="httpClient">Instância de HttpClient usada para realizar chamadas HTTP à API.</param>
|
||||||
public Fatura(string id, JsonElement fatura_Parsed, string fatura_path)
|
|
||||||
{
|
|
||||||
// Utilizado para gerar novo token
|
|
||||||
// this.token = Req_token(HttpClient).ToString();
|
|
||||||
faturaParsed = fatura_Parsed;
|
|
||||||
Agrupada = faturaParsed.TryGetProperty("multiple", out var _);
|
|
||||||
Status = faturaParsed.GetProperty("newStatus").GetString();
|
|
||||||
Arquivo = new FileInfo(fatura_path);
|
|
||||||
this.id = id;
|
|
||||||
|
|
||||||
if (faturaParsed.TryGetProperty("external", out var _))
|
|
||||||
{
|
|
||||||
pagina = JsonDocument.Parse(faturaParsed.GetProperty("external").GetString() !).RootElement.GetProperty("origin")[0].GetProperty("page").GetInt32();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Agrupada)
|
|
||||||
{
|
|
||||||
Agrupada_children = faturaParsed.GetProperty("children").EnumerateArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public Fatura(string id, string fatura_path, HttpClient httpClient)
|
public Fatura(string id, string fatura_path, HttpClient httpClient)
|
||||||
{
|
{
|
||||||
// Utilizado para gerar novo token
|
// Utilizado para gerar novo token
|
||||||
@ -78,6 +58,7 @@ namespace Faturas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Fatura"/> class.
|
/// Initializes a new instance of the <see cref="Fatura"/> class.
|
||||||
/// Inicializa uma nova instância da classe <see cref="Fatura"/> para faturas já processadas.
|
/// Inicializa uma nova instância da classe <see cref="Fatura"/> para faturas já processadas.
|
||||||
@ -163,20 +144,36 @@ namespace Faturas
|
|||||||
|
|
||||||
dadosTusd.Mes = int.Parse(parsedResult.dates.reading.periodUntil.AddDays(-15).ToString("yMM"));
|
dadosTusd.Mes = int.Parse(parsedResult.dates.reading.periodUntil.AddDays(-15).ToString("yMM"));
|
||||||
|
|
||||||
string uc = new Regex("^0+").Replace(parsedResult.locationNumber, string.Empty).Replace("/", string.Empty).Replace("-", string.Empty).Replace(".", string.Empty);
|
string uc = "%" + new Regex("^0+").Replace(parsedResult.locationNumber, string.Empty).Replace("/", string.Empty).Replace("-", string.Empty).Replace(".", string.Empty) + "%";
|
||||||
|
|
||||||
// Vinculo da fatura com os dados cadastrais
|
// Vinculo da fatura com os dados cadastrais
|
||||||
int? unidades;
|
int? unidades;
|
||||||
string sqlQuery = $"SELECT COUNT (Cod_Smart_unidade) FROM Dados_cadastrais WHERE Codigo_Instalacao = @uc AND unidade_gerenciada";
|
string sqlQuery = $"SELECT COUNT (Cod_Smart_unidade) FROM Dados_cadastrais WHERE Codigo_Instalacao LIKE @uc AND unidade_gerenciada";
|
||||||
using (OleDbCommand cmd = new (sqlQuery, conn))
|
using (OleDbCommand cmd = new (sqlQuery, conn))
|
||||||
{
|
{
|
||||||
cmd.Parameters.AddWithValue("@uc", uc);
|
cmd.Parameters.AddWithValue("@uc", uc);
|
||||||
|
|
||||||
unidades = (int?)cmd.ExecuteScalar();
|
unidades = (int?)cmd.ExecuteScalar();
|
||||||
|
|
||||||
|
}
|
||||||
if (unidades == 1)
|
if (unidades == 1)
|
||||||
{
|
{
|
||||||
sqlQuery = $"SELECT Cod_Smart_unidade, Gestao, Cliente, Unidade, PerfilCCEE, Submercado, Status_unidade, Grupo, Perfil, Distribuidora, ICMS_TUSD, Demanda_P, Demanda_FP, Caminho_NFs, Data_de_Migracao FROM Dados_cadastrais WHERE Codigo_Instalacao = @uc AND unidade_gerenciada";
|
sqlQuery = $"SELECT Cod_Smart_unidade, Gestao, Cliente, Unidade, PerfilCCEE, Submercado, Status_unidade, Grupo, Perfil, Distribuidora, ICMS_TUSD, Demanda_P, Demanda_FP, Caminho_NFs, Data_de_Migracao FROM Dados_cadastrais WHERE Codigo_Instalacao LIKE @uc AND unidade_gerenciada";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uc = parsedResult.locationNumber;
|
||||||
|
sqlQuery = $"SELECT COUNT (Cod_Smart_unidade) FROM Dados_cadastrais WHERE new_number LIKE @uc AND unidade_gerenciada";
|
||||||
|
using (OleDbCommand cmd = new(sqlQuery, conn))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@uc", uc);
|
||||||
|
|
||||||
|
unidades = (int?)cmd.ExecuteScalar();
|
||||||
|
|
||||||
|
}
|
||||||
|
if (unidades == 1)
|
||||||
|
{
|
||||||
|
sqlQuery = $"SELECT Cod_Smart_unidade, Gestao, Cliente, Unidade, PerfilCCEE, Submercado, Status_unidade, Grupo, Perfil, Distribuidora, ICMS_TUSD, Demanda_P, Demanda_FP, Caminho_NFs, Data_de_Migracao FROM Dados_cadastrais WHERE new_number LIKE @uc AND unidade_gerenciada";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
namespace Download_Faturas
|
namespace Download_Faturas
|
||||||
{
|
{
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@ -134,19 +133,7 @@ namespace Download_Faturas
|
|||||||
};
|
};
|
||||||
multipartContent.ElementAt(0).Headers.Add("Content-Type", "application/pdf");
|
multipartContent.ElementAt(0).Headers.Add("Content-Type", "application/pdf");
|
||||||
|
|
||||||
var payload = new
|
multipartContent.Add(new StringContent($"{{\"callbackUrl\":\"https://api.4docs.cloud/v2/null\",\"pipelineName\":\"energy\",\"multiple\":{agrupada.ToString().ToLower()},\"clientData\":{{\"fatura_PATH\":\"{fatura.Replace(",", string.Empty)}\"}}}}"), "json");
|
||||||
{
|
|
||||||
callbackUrl = "https://4docs.webhook.smartenergia.com.br/api",
|
|
||||||
pipelineName = "energy",
|
|
||||||
multiple = agrupada,
|
|
||||||
clientData = new
|
|
||||||
{
|
|
||||||
fatura_PATH = fatura.Replace(",", string.Empty).Replace("1 - INDIVIDUAIS", "3 - PROCESSANDO"),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var json = JsonSerializer.Serialize(payload);
|
|
||||||
multipartContent.Add(new StringContent(json, Encoding.UTF8, "application/json"), "json");
|
|
||||||
|
|
||||||
request.Content = multipartContent;
|
request.Content = multipartContent;
|
||||||
|
|
||||||
|
|||||||
55
Webhook 4docs/Migrations/20260227164052_AddJsonBodyToProcessedInvoices.Designer.cs
generated
Normal file
55
Webhook 4docs/Migrations/20260227164052_AddJsonBodyToProcessedInvoices.Designer.cs
generated
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Webhook_4docs;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Webhook_4docs.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(WebhookDbContext))]
|
||||||
|
[Migration("20260227164052_AddJsonBodyToProcessedInvoices")]
|
||||||
|
partial class AddJsonBodyToProcessedInvoices
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "9.0.0")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Webhook_4docs.ProcessedInvoices", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("InvoiceId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("InvoiceId"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateTimeProcessed")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("InvoicePath")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("JsonBody")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("InvoiceId");
|
||||||
|
|
||||||
|
b.ToTable("ProcessedInvoices");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Webhook_4docs.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddJsonBodyToProcessedInvoices : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "JsonBody",
|
||||||
|
table: "ProcessedInvoices",
|
||||||
|
type: "jsonb",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "JsonBody",
|
||||||
|
table: "ProcessedInvoices");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
using Webhook_4docs;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
@ -16,12 +17,12 @@ namespace Webhook_4docs.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "7.0.17")
|
.HasAnnotation("ProductVersion", "9.0.0")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.Entity("ProcessedInvoices", b =>
|
modelBuilder.Entity("Webhook_4docs.ProcessedInvoices", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("InvoiceId")
|
b.Property<int>("InvoiceId")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@ -35,6 +36,9 @@ namespace Webhook_4docs.Migrations
|
|||||||
b.Property<string>("InvoicePath")
|
b.Property<string>("InvoicePath")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("JsonBody")
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
b.Property<string>("Status")
|
b.Property<string>("Status")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Webhook_4docs
|
namespace Webhook_4docs
|
||||||
{
|
{
|
||||||
@ -9,5 +10,6 @@ namespace Webhook_4docs
|
|||||||
public DateTime DateTimeProcessed { get; set; }
|
public DateTime DateTimeProcessed { get; set; }
|
||||||
public string? Status { get; set; }
|
public string? Status { get; set; }
|
||||||
public string? InvoicePath { get; set; }
|
public string? InvoicePath { get; set; }
|
||||||
|
public JsonDocument? JsonBody { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -13,14 +13,33 @@ namespace Webhook_4docs
|
|||||||
{
|
{
|
||||||
PermitLimit = 1
|
PermitLimit = 1
|
||||||
});
|
});
|
||||||
|
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
static string IndexedFilename(string stub, string extension)
|
||||||
|
{
|
||||||
|
int ix = 0;
|
||||||
|
string? filename = null;
|
||||||
|
|
||||||
|
if (File.Exists(String.Format("{0}.{1}", stub, extension)))
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ix++;
|
||||||
|
filename = String.Format("{0} ({1}).{2}", stub, ix, extension);
|
||||||
|
} while (File.Exists(filename));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filename = String.Format("{0}.{1}", stub, extension);
|
||||||
|
}
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
builder.Configuration.AddJsonFile("appsettings.json");
|
builder.Configuration.AddJsonFile("appsettings.json");
|
||||||
|
|
||||||
var connectionString = builder.Configuration.GetConnectionString("WebhookDbContext");
|
var connectionString = builder.Configuration.GetConnectionString("WebhookDbContext");
|
||||||
var appFolder = builder.Configuration["PathBase"];
|
string? appFolder = builder.Configuration["PathBase"];
|
||||||
|
|
||||||
builder.Services.AddDbContext<WebhookDbContext>(options =>
|
builder.Services.AddDbContext<WebhookDbContext>(options =>
|
||||||
options.UseNpgsql(connectionString)
|
options.UseNpgsql(connectionString)
|
||||||
@ -32,382 +51,215 @@ namespace Webhook_4docs
|
|||||||
loggingBuilder.AddConsole();
|
loggingBuilder.AddConsole();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
var logger = app.Services.GetRequiredService<ILogger<Program>>();
|
||||||
|
|
||||||
using (var scope = app.Services.CreateScope())
|
using (var scope = app.Services.CreateScope())
|
||||||
{
|
{
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<WebhookDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<WebhookDbContext>();
|
||||||
|
dbContext.Database.EnsureCreated();
|
||||||
dbContext.Database.Migrate();
|
dbContext.Database.Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string fatura_arquivo;
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseExceptionHandler("/Error");
|
app.UseExceptionHandler("/Error");
|
||||||
|
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||||
app.UseHsts();
|
app.UseHsts();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(appFolder))
|
|
||||||
{
|
|
||||||
app.UsePathBase(appFolder);
|
app.UsePathBase(appFolder);
|
||||||
}
|
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.MapRazorPages();
|
app.MapRazorPages();
|
||||||
|
|
||||||
// PATCH /api -> health ou invoice (json)
|
app.Use((context, next) =>
|
||||||
app.MapPatch("/api", async context =>
|
|
||||||
{
|
{
|
||||||
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
|
context.Request.PathBase = new PathString(appFolder);
|
||||||
var bodyText = await SafeReadBodyAsync(context.Request, logger);
|
return next(context);
|
||||||
if (bodyText is null)
|
|
||||||
{
|
|
||||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
||||||
await context.Response.WriteAsync("Invalid JSON");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryParseJson(bodyText, out var root, logger))
|
|
||||||
{
|
|
||||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
||||||
await context.Response.WriteAsync("Invalid JSON");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Health: healthy + blame + locationID
|
|
||||||
bool isHealth =
|
|
||||||
root.TryGetProperty("healthy", out _) &&
|
|
||||||
root.TryGetProperty("blame", out _) &&
|
|
||||||
root.TryGetProperty("locationID", out _);
|
|
||||||
|
|
||||||
if (isHealth)
|
|
||||||
{
|
|
||||||
await ProcessHealthAsync(root, GetCaminhoDb(), logger);
|
|
||||||
context.Response.StatusCode = StatusCodes.Status200OK;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invoice: requestID/requestId + json
|
|
||||||
bool hasRequestId = root.TryGetProperty("requestID", out var reqIdEl);
|
|
||||||
bool hasJson = root.TryGetProperty("json", out var dataEl);
|
|
||||||
|
|
||||||
if (hasRequestId && hasJson)
|
|
||||||
{
|
|
||||||
await ProcessInvoiceAsync(root, dataEl, GetCaminhoDb(), logger, app, reqIdEl.ToString());
|
|
||||||
|
|
||||||
context.Response.StatusCode = StatusCodes.Status200OK;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.LogDebug("PATCH /api payload não reconhecido.");
|
|
||||||
context.Response.StatusCode = StatusCodes.Status202Accepted;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// PUT /api -> somente invoice (result)
|
// Endpoint para \api
|
||||||
app.MapPut("/api", async context =>
|
app.UseEndpoints(endpoints =>
|
||||||
{
|
{
|
||||||
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
|
endpoints.MapControllers();
|
||||||
var bodyText = await SafeReadBodyAsync(context.Request, logger);
|
endpoints.MapPatch("/api", async context =>
|
||||||
if (bodyText is null)
|
{
|
||||||
|
|
||||||
|
string requestBody;
|
||||||
|
using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true))
|
||||||
|
{
|
||||||
|
requestBody = await reader.ReadToEndAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var JsonBody = JsonDocument.Parse(requestBody).RootElement;
|
||||||
|
var JsonBodyDocument = JsonDocument.Parse(requestBody);
|
||||||
|
string CaminhoDB = "X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb";
|
||||||
|
|
||||||
|
if (JsonBody.TryGetProperty("requestID", out JsonElement fatura_ID_json))
|
||||||
|
{
|
||||||
|
string fatura_ID = fatura_ID_json.ToString();
|
||||||
|
|
||||||
|
if (!JsonBody.TryGetProperty("json", out JsonElement root)) { return; }
|
||||||
|
|
||||||
|
JsonElement DadosJson = JsonDocument.Parse(root.ToString()).RootElement;
|
||||||
|
|
||||||
|
string tipoDocumento = root.Get("documentType").ToString() ?? string.Empty.ToLower();
|
||||||
|
|
||||||
|
if (tipoDocumento != string.Empty && tipoDocumento != "nota_fiscal")
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
||||||
await context.Response.WriteAsync("Invalid JSON");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryParseJson(bodyText, out var root, logger))
|
Fatura fatura = new(fatura_ID, JsonBody);
|
||||||
{
|
bool completed = false;
|
||||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
||||||
await context.Response.WriteAsync("Invalid JSON");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invoice: requestID/requestId + result
|
|
||||||
bool hasRequestId = root.TryGetProperty("requestId", out var reqIdEl);
|
|
||||||
bool hasResult = root.TryGetProperty("result", out var dataEl);
|
|
||||||
|
|
||||||
if (!(hasRequestId && hasResult))
|
|
||||||
{
|
|
||||||
logger.LogDebug("PUT /api payload inválido para este endpoint.");
|
|
||||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await ProcessInvoiceAsync(root, dataEl, GetCaminhoDb(), logger, app, reqIdEl.ToString());
|
|
||||||
|
|
||||||
context.Response.StatusCode = StatusCodes.Status200OK;
|
|
||||||
});
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
|
|
||||||
string GetCaminhoDb() =>
|
|
||||||
@"X:/Middle/Informativo Setorial/Modelo Word/BD1_dados cadastrais e faturas.accdb";
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== Utilitários de request/JSON =====
|
|
||||||
static async Task<string?> SafeReadBodyAsync(HttpRequest request, ILogger logger, int maxBytes = 10 * 1024 * 1024)
|
|
||||||
{
|
|
||||||
request.EnableBuffering();
|
|
||||||
using var ms = new MemoryStream(capacity: Math.Min(maxBytes, 1024 * 64));
|
|
||||||
await request.Body.CopyToAsync(ms);
|
|
||||||
if (ms.Length > maxBytes)
|
|
||||||
{
|
|
||||||
logger.LogWarning("Payload excede o limite de {MaxBytes} bytes.", maxBytes);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
ms.Position = 0;
|
|
||||||
using var reader = new StreamReader(ms, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true);
|
|
||||||
return await reader.ReadToEndAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool TryParseJson(string text, out JsonElement root, ILogger logger)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
root = JsonDocument.Parse(text).RootElement.Clone();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (JsonException ex)
|
|
||||||
{
|
|
||||||
logger.LogWarning(ex, "Falha ao parsear JSON.");
|
|
||||||
root = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== Processamento de Invoice =====
|
|
||||||
static async Task ProcessInvoiceAsync(JsonElement root, JsonElement dataEl, string caminhoDB, ILogger logger, WebApplication app, string fatura_ID)
|
|
||||||
{
|
|
||||||
var tipoDocumento = dataEl.Get("documentType")?.GetString()?.ToLowerInvariant()
|
|
||||||
?? string.Empty;
|
|
||||||
|
|
||||||
var deveProcessar = string.IsNullOrEmpty(tipoDocumento) || tipoDocumento == "nota_fiscal";
|
|
||||||
if (!deveProcessar)
|
|
||||||
{
|
|
||||||
logger.LogInformation("Invoice ignorada por tipoDocumento: {TipoDocumento}", tipoDocumento);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasPath = !string.IsNullOrEmpty(root.GetProperty("clientData").ToString());
|
|
||||||
|
|
||||||
|
|
||||||
if (hasPath)
|
|
||||||
{
|
|
||||||
root.GetProperty("clientData").TryGetProperty("fatura_PATH", out var pathEl);
|
|
||||||
await ProcessInvoiceFromPath(root, fatura_ID, pathEl!.GetString()!, caminhoDB, logger, app);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await ProcessInvoiceFromJson(root, fatura_ID, caminhoDB, logger, app);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static async Task ProcessInvoiceFromPath(JsonElement root, string fatura_ID, string pdfPath, string caminhoDB, ILogger logger, WebApplication app)
|
|
||||||
{
|
|
||||||
var fatura = new Fatura(fatura_ID, root, pdfPath);
|
|
||||||
var completed = false;
|
|
||||||
|
|
||||||
while (!completed)
|
while (!completed)
|
||||||
{
|
{
|
||||||
var connLease = await connRateLimiter.AcquireAsync();
|
var connLease = await connRateLimiter.AcquireAsync();
|
||||||
if (!connLease.IsAcquired) continue;
|
|
||||||
|
|
||||||
try
|
if (connLease.IsAcquired)
|
||||||
|
{
|
||||||
|
using (OleDbConnection conn = new(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + CaminhoDB + ";Jet OLEDB:Database Password=gds21"))
|
||||||
|
{
|
||||||
|
if (conn != null)
|
||||||
{
|
{
|
||||||
using var conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + caminhoDB + ";Jet OLEDB:Database Password=gds21");
|
|
||||||
if (conn.State == System.Data.ConnectionState.Closed)
|
if (conn.State == System.Data.ConnectionState.Closed)
|
||||||
{
|
{
|
||||||
await conn.OpenAsync();
|
await conn.OpenAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
fatura.Processar(conn);
|
fatura.Processar(conn);
|
||||||
completed = true;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
completed = true;
|
completed = true;
|
||||||
|
connLease.Dispose();
|
||||||
throw new Exception(ex.Message);
|
throw new Exception(ex.Message);
|
||||||
}
|
}
|
||||||
finally
|
}
|
||||||
{
|
}
|
||||||
|
completed = true;
|
||||||
connLease.Dispose();
|
connLease.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await FinalizarPersistenciaAsync(fatura, fatura_ID, async targetPath =>
|
string? status = fatura.Status;
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!File.Exists(targetPath))
|
|
||||||
{
|
|
||||||
fatura.Mover(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioEx)
|
|
||||||
{
|
|
||||||
logger.LogWarning(ioEx, "Falha ao copiar arquivo para {Target}", targetPath);
|
|
||||||
}
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}, logger, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async Task ProcessInvoiceFromJson(JsonElement root, string fatura_ID, string caminhoDB, ILogger logger, WebApplication app)
|
int status_id = 0;
|
||||||
{
|
|
||||||
var fatura = new Fatura(fatura_ID, root);
|
|
||||||
var completed = false;
|
|
||||||
|
|
||||||
while (!completed)
|
switch (status)
|
||||||
{
|
{
|
||||||
var connLease = await connRateLimiter.AcquireAsync();
|
case ("FATURA DUPLICADA NO BD"):
|
||||||
if (!connLease.IsAcquired) continue;
|
status_id = 4;
|
||||||
|
break;
|
||||||
|
case ("UNIDADE CONSUMIDORA NÃO LOCALIZADA NO BD"):
|
||||||
|
status_id = 5;
|
||||||
|
break;
|
||||||
|
case ("FATURA INCLUIDA NO BD"):
|
||||||
|
status_id = 6;
|
||||||
|
break;
|
||||||
|
case (_):
|
||||||
|
status = "ERRO";
|
||||||
|
status_id = 7;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var conn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + caminhoDB + ";Jet OLEDB:Database Password=gds21");
|
string path = $@"X:\Middle\Carteira {fatura.Gestao![0]}\Carteira {fatura.Gestao}\Faturas fourdocs\{status_id} - {status}";
|
||||||
if (conn.State == System.Data.ConnectionState.Closed)
|
|
||||||
{
|
|
||||||
await conn.OpenAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
fatura.Processar(conn);
|
|
||||||
completed = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
completed = true;
|
|
||||||
throw new Exception(ex.Message);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
connLease.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await FinalizarPersistenciaAsync(fatura, fatura_ID, async targetPath =>
|
|
||||||
{
|
|
||||||
var base64 = root.Get("pdfFile")?.GetString();
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(base64))
|
|
||||||
{
|
|
||||||
logger.LogWarning("Campo pdfFile/pdfFileBase64 ausente no payload.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CriarArquivo(targetPath, base64!);
|
|
||||||
await Task.CompletedTask;
|
|
||||||
}, logger, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async Task FinalizarPersistenciaAsync(Fatura fatura, string fatura_ID, Func<string, Task> persistFile, ILogger logger, WebApplication app)
|
|
||||||
{
|
|
||||||
var status = fatura.Status;
|
|
||||||
var status_id = status switch
|
|
||||||
{
|
|
||||||
"FATURA DUPLICADA NO BD" => 4,
|
|
||||||
"UNIDADE CONSUMIDORA NÃO LOCALIZADA NO BD" => 5,
|
|
||||||
"FATURA INCLUIDA NO BD" => 6,
|
|
||||||
_ => 7
|
|
||||||
};
|
|
||||||
status = status_id == 7 ? "ERRO" : status;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var pathBase = $@"X:\Middle\Carteira {fatura.Gestao![0]}\Carteira {fatura.Gestao}\Faturas fourdocs\{status_id} - {status}";
|
|
||||||
if (status_id == 6 && fatura.PastaTUSD!.Exists)
|
if (status_id == 6 && fatura.PastaTUSD!.Exists)
|
||||||
{
|
{
|
||||||
pathBase = fatura.PastaTUSD!.FullName;
|
path = fatura.PastaTUSD!.FullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status_id != 4)
|
if (status_id != 4)
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(pathBase);
|
fatura_arquivo = IndexedFilename($@"{path}\ID {fatura_ID!} - Mês {fatura.Mes} - Empresa {fatura.Empresa} - Unidade {fatura.Unidade}", "pdf");
|
||||||
var targetFile = IndexedFilename($@"{pathBase}\ID {fatura_ID!} - Mês {fatura.Mes} - Empresa {fatura.Empresa} - Unidade {fatura.Unidade}", "pdf");
|
CriarArquivo(fatura_arquivo, JsonBody.GetProperty("pdfFile").ToString());
|
||||||
|
|
||||||
await persistFile(targetFile);
|
var DatabaseModel = new ProcessedInvoices
|
||||||
|
|
||||||
var dbModel = new ProcessedInvoices
|
|
||||||
{
|
{
|
||||||
InvoiceId = int.Parse(fatura_ID),
|
InvoiceId = Int32.Parse(fatura_ID),
|
||||||
DateTimeProcessed = DateTime.UtcNow,
|
DateTimeProcessed = DateTime.UtcNow,
|
||||||
Status = status,
|
Status = status,
|
||||||
InvoicePath = targetFile
|
InvoicePath = fatura_arquivo,
|
||||||
|
JsonBody = JsonBodyDocument
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.LogInformation("Fatura incluída no BD");
|
logger.LogInformation("Fatura incluída no BD");
|
||||||
logger.LogInformation("Fatura salva na pasta {Arquivo}", targetFile);
|
|
||||||
|
|
||||||
using var scope = app.Services.CreateScope();
|
logger.LogInformation("Fatura salva na pasta " + fatura_arquivo);
|
||||||
|
|
||||||
|
using (var scope = app.Services.CreateScope())
|
||||||
|
{
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<WebhookDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<WebhookDbContext>();
|
||||||
dbContext.ProcessedInvoices.Add(dbModel);
|
dbContext.ProcessedInvoices.Add(DatabaseModel);
|
||||||
await dbContext.SaveChangesAsync();
|
await dbContext.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogInformation("Fatura duplicada no BD");
|
logger.LogInformation("Fatura duplicada no BD");
|
||||||
|
|
||||||
logger.LogInformation("Fatura descartada");
|
logger.LogInformation("Fatura descartada");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
logger.LogError(ex, "Erro no processamento da fatura");
|
logger.LogError("Erro no processamento da fatura");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (JsonBody.TryGetProperty("healthy", out _) && JsonBody.TryGetProperty("blame", out _) && JsonBody.TryGetProperty("locationID", out _))
|
||||||
|
{
|
||||||
|
double errorID = 0;
|
||||||
|
|
||||||
public static void CriarArquivo(string fatura_arquivo, string pdfFile64)
|
switch (JsonBody.GetProperty("healthy").GetBoolean(), JsonBody.GetProperty("blame").ToString().ToLower())
|
||||||
{
|
{
|
||||||
if (!File.Exists(fatura_arquivo))
|
case (false, "user"):
|
||||||
{
|
logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Sem acesso");
|
||||||
byte[] bytes = Convert.FromBase64String(pdfFile64);
|
errorID = 1;
|
||||||
using var stream = new FileStream(fatura_arquivo, FileMode.CreateNew, FileAccess.Write, FileShare.None);
|
break;
|
||||||
using var writer = new BinaryWriter(stream);
|
case (false, "system"):
|
||||||
writer.Write(bytes, 0, bytes.Length);
|
logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Com erro de sistema");
|
||||||
}
|
errorID = 2;
|
||||||
|
break;
|
||||||
|
case (false, _):
|
||||||
|
logger.LogError("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " Com erro " + JsonBody.GetProperty("blame").ToString());
|
||||||
|
errorID = 3;
|
||||||
|
break;
|
||||||
|
case (true, _):
|
||||||
|
//logger.LogInformation("Loc ID: " + JsonBody.GetProperty("locationID").ToString() + " está saudável");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string IndexedFilename(string stub, string extension)
|
var errorText = GetErrorText(JsonBody);
|
||||||
{
|
await UpdateErrorIdStatusAsync(CaminhoDB, JsonBody.GetProperty("locationID").GetInt64(), errorID, errorText, logger);
|
||||||
int ix = 0;
|
|
||||||
string? filename;
|
|
||||||
|
|
||||||
if (File.Exists($"{stub}.{extension}"))
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
ix++;
|
|
||||||
filename = $"{stub} ({ix}).{extension}";
|
|
||||||
} while (File.Exists(filename));
|
|
||||||
}
|
}
|
||||||
else
|
});
|
||||||
|
//implementação futura para receber as faturas enviadas via api de forma assíncrona - Upload4Docs (substituir parte do agendador de tarefas)
|
||||||
|
endpoints.MapPut("/api", async context =>
|
||||||
{
|
{
|
||||||
filename = $"{stub}.{extension}";
|
var a = 10;
|
||||||
}
|
a++;
|
||||||
return filename;
|
await Task.CompletedTask;
|
||||||
}
|
});
|
||||||
|
});
|
||||||
// ===== Processamento de Health =====
|
app.Run();
|
||||||
static async Task ProcessHealthAsync(JsonElement jsonBody, string caminhoDB, ILogger logger)
|
|
||||||
{
|
|
||||||
var healthy = jsonBody.GetProperty("healthy").GetBoolean();
|
|
||||||
var blame = jsonBody.GetProperty("blame").GetString()?.ToLowerInvariant() ?? string.Empty;
|
|
||||||
var locationId = jsonBody.GetProperty("locationID").ToString();
|
|
||||||
|
|
||||||
var (errorID, logAction) = (healthy, blame) switch
|
|
||||||
{
|
|
||||||
(false, "user") => (1d, (Action)(() => logger.LogError("Loc ID: {LocationId} Sem acesso", locationId))),
|
|
||||||
(false, "system") => (2d, (Action)(() => logger.LogError("Loc ID: {LocationId} Com erro de sistema", locationId))),
|
|
||||||
(false, var other) => (3d, (Action)(() => logger.LogError("Loc ID: {LocationId} Com erro {Blame}", locationId, other))),
|
|
||||||
(true, _) => (0d, (Action)(() => { }))
|
|
||||||
};
|
|
||||||
|
|
||||||
logAction();
|
|
||||||
|
|
||||||
var errorText = GetErrorText(jsonBody);
|
|
||||||
await UpdateErrorIdStatusAsync(caminhoDB, jsonBody.GetProperty("locationID").GetInt64(), errorID, errorText, logger);
|
|
||||||
}
|
}
|
||||||
//tenta pegar o deactivationReport, se não conseguir ou for null, tenta pegar o websiteText, se não conseguir ou for null, tenta pegar o systemText. As propriedades podem não exisir
|
//tenta pegar o deactivationReport, se não conseguir ou for null, tenta pegar o websiteText, se não conseguir ou for null, tenta pegar o systemText. As propriedades podem não exisir
|
||||||
public static string GetErrorText(JsonElement root)
|
public static string GetErrorText(JsonElement root)
|
||||||
@ -433,7 +285,6 @@ namespace Webhook_4docs
|
|||||||
|
|
||||||
return errorText;
|
return errorText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<int> UpdateErrorIdStatusAsync(string CaminhoDB, double location_id, double errorID, string systemText, ILogger logger)
|
public static async Task<int> UpdateErrorIdStatusAsync(string CaminhoDB, double location_id, double errorID, string systemText, ILogger logger)
|
||||||
{
|
{
|
||||||
int test = 0;
|
int test = 0;
|
||||||
@ -457,26 +308,31 @@ namespace Webhook_4docs
|
|||||||
SET errorID = @errorID, status = @status
|
SET errorID = @errorID, status = @status
|
||||||
WHERE location_id = @location_id", conn))
|
WHERE location_id = @location_id", conn))
|
||||||
{
|
{
|
||||||
|
// Adiciona parâmetros de forma segura
|
||||||
cmd.Parameters.AddWithValue("@errorID", errorID);
|
cmd.Parameters.AddWithValue("@errorID", errorID);
|
||||||
cmd.Parameters.AddWithValue("@status", systemText);
|
cmd.Parameters.AddWithValue("@status", systemText);
|
||||||
cmd.Parameters.AddWithValue("@location_id", location_id);
|
cmd.Parameters.AddWithValue("@location_id", location_id);
|
||||||
|
|
||||||
|
// Executa o comando e captura o resultado
|
||||||
test = await cmd.ExecuteNonQueryAsync();
|
test = await cmd.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break; // Sai do loop em caso de sucesso
|
||||||
}
|
}
|
||||||
catch (OleDbException ex)
|
catch (OleDbException ex)
|
||||||
{
|
{
|
||||||
|
// Registra o erro
|
||||||
logger.LogInformation($"Erro no OleDb update: {ex.Message} (Tentativa {attempt + 1} de {maxRetries})");
|
logger.LogInformation($"Erro no OleDb update: {ex.Message} (Tentativa {attempt + 1} de {maxRetries})");
|
||||||
|
|
||||||
if (attempt < maxRetries - 1)
|
if (attempt < maxRetries - 1)
|
||||||
{
|
{
|
||||||
await Task.Delay(1000 * (int)Math.Pow(2, attempt));
|
// Aguarda antes de tentar novamente
|
||||||
|
await Task.Delay(1000 * (int)Math.Pow(2, attempt)); // Retry com Backoff Exponencial
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Propaga a exceção na última tentativa
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,17 +341,32 @@ namespace Webhook_4docs
|
|||||||
connLease.Dispose();
|
connLease.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt++;
|
attempt++; // Incrementa a tentativa após adquirir o lease, mesmo que falhe
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Aguarda um curto período antes de tentar novamente se não conseguiu adquirir o lease
|
||||||
await Task.Delay(200);
|
await Task.Delay(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
}
|
public static void CriarArquivo(string fatura_arquivo, string pdfFile64)
|
||||||
|
{
|
||||||
|
//string fatura_arquivo = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"\test.pdf";
|
||||||
|
if (!File.Exists(fatura_arquivo))
|
||||||
|
{
|
||||||
|
byte[] bytes = Convert.FromBase64String(pdfFile64);
|
||||||
|
|
||||||
|
System.IO.FileStream stream = new(fatura_arquivo, FileMode.CreateNew);
|
||||||
|
System.IO.BinaryWriter writer =
|
||||||
|
new(stream);
|
||||||
|
writer.Write(bytes, 0, bytes.Length);
|
||||||
|
writer.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
public static partial class JsonExtensions
|
public static partial class JsonExtensions
|
||||||
{
|
{
|
||||||
public static JsonElement? Get(this JsonElement element, string name) =>
|
public static JsonElement? Get(this JsonElement element, string name) =>
|
||||||
@ -506,7 +377,7 @@ namespace Webhook_4docs
|
|||||||
{
|
{
|
||||||
if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined)
|
if (element.ValueKind == JsonValueKind.Null || element.ValueKind == JsonValueKind.Undefined)
|
||||||
return null;
|
return null;
|
||||||
|
// Throw if index < 0
|
||||||
return index < element.GetArrayLength() ? element[index] : null;
|
return index < element.GetArrayLength() ? element[index] : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,25 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace Webhook_4docs
|
namespace Webhook_4docs
|
||||||
{
|
{
|
||||||
public class WebhookDbContext(DbContextOptions<WebhookDbContext> options) : DbContext(options)
|
public class WebhookDbContext(DbContextOptions<WebhookDbContext> options) : DbContext(options)
|
||||||
{
|
{
|
||||||
public DbSet<ProcessedInvoices> ProcessedInvoices { get; set; }
|
public DbSet<ProcessedInvoices> ProcessedInvoices { get; set; }
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity<ProcessedInvoices>()
|
||||||
|
.Property(p => p.JsonBody)
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasConversion(
|
||||||
|
v => v == null ? null : v.RootElement.GetRawText(),
|
||||||
|
v => v == null ? null : ParseJsonDocument(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonDocument ParseJsonDocument(string json)
|
||||||
|
=> JsonDocument.Parse(json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
@ -17,7 +17,7 @@
|
|||||||
"Kestrel": {
|
"Kestrel": {
|
||||||
"Endpoints": {
|
"Endpoints": {
|
||||||
"HttpLocalhost": {
|
"HttpLocalhost": {
|
||||||
"Url": "http://*:8664/"
|
"Url": "http://localhost:8662/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user