129 lines
5.0 KiB
C#
129 lines
5.0 KiB
C#
using Newtonsoft.Json;
|
|
using SmartB2B.Enova.Contracts;
|
|
using SmartB2B.Enova.Service.Diagnostics;
|
|
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 });
|
|
}
|
|
|
|
ConsoleLogger.Verbose(
|
|
$"ZO: żądanie zweryfikowane; definicja={request.DocumentDefinition}, " +
|
|
$"magazyn={request.WarehouseCode ?? "(standardowy)"}, tryb={request.SaveMode}.");
|
|
ConsoleLogger.Verbose("ZO: oczekiwanie na wyłączną sekcję operacji Enovy.");
|
|
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
|
|
{
|
|
ConsoleLogger.Verbose("ZO: przekazanie żądania do adaptera Enovy.");
|
|
result = await Task.Run(() => _adapter.CreateOrder(request), cancellationToken)
|
|
.ConfigureAwait(false);
|
|
}
|
|
catch (EnovaOperationException exception)
|
|
{
|
|
ConsoleLogger.Error(
|
|
$"Błąd tworzenia ZO [{exception.ErrorUri}]: {exception.Message}",
|
|
exception);
|
|
await SentryReporter.CaptureAsync(exception, scope =>
|
|
{
|
|
scope.SetTag("rpc.procedure", "eu.smartb2b.place_order");
|
|
scope.SetTag("wamp.error_uri", exception.ErrorUri);
|
|
})
|
|
.ConfigureAwait(false);
|
|
throw WampErrorFactory.FromEnova(exception);
|
|
}
|
|
|
|
Console.WriteLine($"Utworzono ZO {result.Number} (ID: {result.Id}).");
|
|
ConsoleLogger.Verbose(
|
|
$"ZO: zapis zakończony; stan={result.SaveMode}, magazyn={result.WarehouseCode}, " +
|
|
$"netto={result.Net}, VAT={result.Vat}, brutto={result.Gross}.");
|
|
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();
|
|
ConsoleLogger.Verbose("ZO: zwolniono wyłączną sekcję operacji Enovy.");
|
|
}
|
|
}
|
|
}
|
|
|
|
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; }
|
|
}
|