Initial Enova365 sync service

This commit is contained in:
2026-07-15 08:27:32 +00:00
commit 605c6b7d2a
50 changed files with 2813 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
using Newtonsoft.Json;
using SmartB2B.Enova.Contracts;
using SmartB2B.Enova.Service.Rpc;
namespace SmartB2B.Enova.Service.Orders;
public sealed class PlaceOrderHandler
{
private readonly IEnovaOrderAdapter _adapter;
private readonly EnovaAdapterConfiguration _configuration;
private readonly SemaphoreSlim _orderLock = new(1, 1);
public PlaceOrderHandler(IEnovaOrderAdapter adapter, EnovaAdapterConfiguration configuration)
{
_adapter = adapter;
_configuration = configuration;
}
public async Task<object?> HandleAsync(RpcInvocation invocation, CancellationToken cancellationToken)
{
if (!invocation.Contains("currency_iso"))
{
throw WampErrorFactory.Create("eu.smartb2b.missing_currency", "Pole currency_iso jest wymagane.");
}
var currency = invocation.Get<string>("currency_iso")?.Trim();
if (!string.Equals(currency, "PLN", StringComparison.OrdinalIgnoreCase))
{
throw WampErrorFactory.Create(
"eu.smartb2b.erp.currency_not_supported",
$"Waluta '{currency}' nie jest obsługiwana. Wymagana waluta: PLN.",
new Dictionary<string, object?> { ["currency_iso"] = currency });
}
var inputLines = invocation.Get<PlaceOrderLine[]>("lines", []) ?? [];
var request = new OrderRequest
{
CustomerCode = invocation.Get<string>("companyErpId") ?? string.Empty,
CustomerReference = invocation.Get<string>("purchase_order_number"),
Notes = invocation.Get<string>("notes"),
DocumentDefinition = _configuration.DocumentDefinition,
WarehouseCode = invocation.Get<string>("warehouseErpId") ?? _configuration.DefaultWarehouseCode,
SaveMode = _configuration.SaveMode,
Items = inputLines.Select(line => new OrderLineRequest
{
ProductCode = line.Symbol ?? string.Empty,
Quantity = line.Quantity,
UnitPrice = line.PriceNetto,
Discount = line.Discount
}).ToArray()
};
var errors = OrderValidator.Validate(request);
if (errors.Count > 0)
{
throw WampErrorFactory.Create(
"eu.smartb2b.erp.invalid_order_lines",
string.Join(" | ", errors),
new Dictionary<string, object?> { ["errors"] = errors });
}
await _orderLock.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
Console.WriteLine(
$"Tworzenie ZO: kontrahent={request.CustomerCode}, pozycje={request.Items.Count}, referencja={request.CustomerReference ?? "(brak)"}.");
OrderResult result;
try
{
result = await Task.Run(() => _adapter.CreateOrder(request), cancellationToken)
.ConfigureAwait(false);
}
catch (EnovaOperationException exception)
{
throw WampErrorFactory.FromEnova(exception);
}
Console.WriteLine($"Utworzono ZO {result.Number} (ID: {result.Id}).");
return new KeywordResult(new Dictionary<string, object?>
{
["order_erp_id"] = result.Id,
["order_erp_symbol"] = result.Number,
["value_netto"] = result.Net,
["value_brutto"] = result.Gross,
["stocks"] = Array.Empty<object>()
});
}
finally
{
_orderLock.Release();
}
}
}
public sealed class PlaceOrderLine
{
[JsonProperty("symbol")]
public string? Symbol { get; init; }
[JsonProperty("quantity")]
public decimal Quantity { get; init; }
[JsonProperty("price_netto")]
public decimal PriceNetto { get; init; }
[JsonProperty("discount")]
public decimal Discount { get; init; }
}