2025-11-27 23:52:32 +01:00
|
|
|
using System.Text.Json;
|
|
|
|
|
using System.Web;
|
|
|
|
|
using BimAI.Domain.Entities;
|
|
|
|
|
using BimAI.Infrastructure.Data;
|
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
|
|
|
|
|
|
namespace BimAI.Infrastructure.Sync;
|
|
|
|
|
|
|
|
|
|
public class InvoiceSyncService(HttpClient httpClient, BimAIDbContext db, IConfiguration configuration)
|
|
|
|
|
{
|
|
|
|
|
private string DecodeHtmlEntities(string text)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(text))
|
|
|
|
|
return text;
|
|
|
|
|
|
|
|
|
|
return HttpUtility.HtmlDecode(text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task RunAsync()
|
|
|
|
|
{
|
2025-11-28 15:11:56 +01:00
|
|
|
var apiKey = configuration["TWINPOL_CRM:ApiKey"];
|
2025-11-27 23:52:32 +01:00
|
|
|
var syncState = db.SyncStates.FirstOrDefault(x => x.Entity == "Invoice") ?? new SyncState { Entity = "Invoice", LastSynced = 0 };
|
|
|
|
|
|
|
|
|
|
var url = $"https://crm.twinpol.com/REST/index.php?key={apiKey}&action=bimai.export.ecommerce&since=0";
|
|
|
|
|
var response = await httpClient.GetStringAsync(url);
|
|
|
|
|
|
|
|
|
|
var jsonDoc = JsonSerializer.Deserialize<JsonElement>(response);
|
|
|
|
|
if (!jsonDoc.TryGetProperty("data", out var dataElement))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("[SYNC] No 'data' property in response");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var invoices = dataElement.EnumerateArray();
|
|
|
|
|
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
|
|
|
|
|
|
|
|
|
foreach (var invoiceJson in invoices)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var e5Id = invoiceJson.GetProperty("e5Id").GetString() ?? "";
|
|
|
|
|
var documentNo = DecodeHtmlEntities(invoiceJson.GetProperty("documentNo").GetString() ?? "");
|
|
|
|
|
var type = DecodeHtmlEntities(invoiceJson.GetProperty("type").GetString() ?? "");
|
|
|
|
|
var registerDateStr = invoiceJson.GetProperty("registerDate").GetString() ?? "";
|
|
|
|
|
var sellDateStr = invoiceJson.GetProperty("sellDate").GetString() ?? "";
|
|
|
|
|
var clientName = DecodeHtmlEntities(invoiceJson.GetProperty("clientName").GetString() ?? "");
|
|
|
|
|
var clientId = DecodeHtmlEntities(invoiceJson.GetProperty("clientId").GetString() ?? "");
|
|
|
|
|
var clientNip = DecodeHtmlEntities(invoiceJson.GetProperty("clientNip").GetString() ?? "");
|
|
|
|
|
var clientAddress = DecodeHtmlEntities(invoiceJson.GetProperty("clientAddress").GetString() ?? "");
|
|
|
|
|
var currency = invoiceJson.GetProperty("currency").GetString() ?? "PLN";
|
|
|
|
|
var totalNettoStr = invoiceJson.GetProperty("totalNetto").GetString() ?? "0";
|
|
|
|
|
var totalBruttoStr = invoiceJson.GetProperty("totalBrutto").GetString() ?? "0";
|
|
|
|
|
var totalVatStr = invoiceJson.GetProperty("totalVat").GetString() ?? "0";
|
|
|
|
|
var source = invoiceJson.GetProperty("source").GetString() ?? "";
|
|
|
|
|
|
|
|
|
|
if (!DateTime.TryParse(registerDateStr, out var registerDate))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"[SYNC] Invalid registerDate for invoice {documentNo}: {registerDateStr}");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!DateTime.TryParse(sellDateStr, out var sellDate))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"[SYNC] Invalid sellDate for invoice {documentNo}: {sellDateStr}");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!decimal.TryParse(totalNettoStr, out var totalNetto))
|
|
|
|
|
totalNetto = 0;
|
|
|
|
|
|
|
|
|
|
if (!decimal.TryParse(totalBruttoStr, out var totalBrutto))
|
|
|
|
|
totalBrutto = 0;
|
|
|
|
|
|
|
|
|
|
if (!decimal.TryParse(totalVatStr, out var totalVat))
|
|
|
|
|
totalVat = 0;
|
|
|
|
|
|
|
|
|
|
var existing = db.Invoices.FirstOrDefault(x => x.DocumentNo == documentNo && x.Source == source);
|
|
|
|
|
|
|
|
|
|
if (existing == null)
|
|
|
|
|
{
|
|
|
|
|
var invoice = new Invoice
|
|
|
|
|
{
|
|
|
|
|
DocumentNo = documentNo,
|
|
|
|
|
Type = type,
|
|
|
|
|
RegisterDate = registerDate,
|
|
|
|
|
SellDate = sellDate,
|
|
|
|
|
ClientName = clientName,
|
|
|
|
|
ClientId = string.IsNullOrWhiteSpace(clientId) ? null : clientId,
|
|
|
|
|
ClientNip = string.IsNullOrWhiteSpace(clientNip) ? null : clientNip,
|
|
|
|
|
ClientAddress = string.IsNullOrWhiteSpace(clientAddress) ? null : clientAddress,
|
|
|
|
|
Currency = currency,
|
|
|
|
|
TotalNetto = totalNetto,
|
|
|
|
|
TotalBrutto = totalBrutto,
|
|
|
|
|
TotalVat = totalVat,
|
|
|
|
|
Source = source,
|
|
|
|
|
CreatedAt = DateTime.UtcNow,
|
|
|
|
|
UpdatedAt = DateTime.UtcNow
|
|
|
|
|
};
|
|
|
|
|
db.Invoices.Add(invoice);
|
|
|
|
|
Console.WriteLine($"[SYNC] Added invoice: {documentNo} from {source}");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
existing.Type = type;
|
|
|
|
|
existing.RegisterDate = registerDate;
|
|
|
|
|
existing.SellDate = sellDate;
|
|
|
|
|
existing.ClientName = clientName;
|
|
|
|
|
existing.ClientId = string.IsNullOrWhiteSpace(clientId) ? null : clientId;
|
|
|
|
|
existing.ClientNip = string.IsNullOrWhiteSpace(clientNip) ? null : clientNip;
|
|
|
|
|
existing.ClientAddress = string.IsNullOrWhiteSpace(clientAddress) ? null : clientAddress;
|
|
|
|
|
existing.Currency = currency;
|
|
|
|
|
existing.TotalNetto = totalNetto;
|
|
|
|
|
existing.TotalBrutto = totalBrutto;
|
|
|
|
|
existing.TotalVat = totalVat;
|
|
|
|
|
existing.UpdatedAt = DateTime.UtcNow;
|
|
|
|
|
Console.WriteLine($"[SYNC] Updated invoice: {documentNo} from {source}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"[SYNC] Error processing invoice: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
syncState.LastSynced = now;
|
|
|
|
|
if (db.SyncStates.FirstOrDefault(x => x.Entity == "Invoice") == null)
|
|
|
|
|
{
|
|
|
|
|
db.SyncStates.Add(syncState);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
db.SyncStates.Update(syncState);
|
|
|
|
|
}
|
|
|
|
|
await db.SaveChangesAsync();
|
|
|
|
|
Console.WriteLine("[SYNC] Invoice sync completed");
|
|
|
|
|
}
|
|
|
|
|
}
|