Add shared ERP sync core and Optima adapter

This commit is contained in:
2026-09-08 02:07:42 +00:00
parent a040a91d1f
commit 6c4e50f7a6
366 changed files with 4707 additions and 478 deletions

View File

@@ -1,13 +1,14 @@
using System.Data.Common;
using System.Reflection;
using System.Runtime.Loader;
using SmartB2B.Enova.Contracts;
using SmartB2B.Sync.Contracts;
using SmartB2B.Sync.Service;
using SmartB2B.Enova.Service;
using SmartB2B.Enova.Service.Configuration;
using SmartB2B.Enova.Service.Orders;
using SmartB2B.Enova.Service.Rpc;
using SmartB2B.Sync.Service.Orders;
using SmartB2B.Sync.Service.Rpc;
using SmartB2B.Enova.Service.Runtime;
using SmartB2B.Enova.Service.Sql;
using SmartB2B.Sync.Service.Sql;
using WampSharp.Core.Serialization;
using WampSharp.V2.Core;
using WampSharp.V2.Core.Contracts;
@@ -84,19 +85,25 @@ static Task SqlProviderIsSupported()
static Task DatabaseRegistrationUsesConnectionString()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
string? installationPath = null;
var installationCandidates = new List<string>();
while (directory is not null)
{
var candidate = Path.Combine(directory.FullName, "enova365 2604.4.4");
if (File.Exists(Path.Combine(candidate, "Soneta.Business.dll")))
{
installationPath = candidate;
break;
}
installationCandidates.Add(Path.Combine(directory.FullName, "enova365 2604.4.4"));
directory = directory.Parent;
}
installationCandidates.Add(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
"Soneta",
"enova365 2604.4.4"));
installationCandidates.Add(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
"Soneta",
"enova365 2604.4.4"));
var installationPath = installationCandidates.FirstOrDefault(
candidate => File.Exists(Path.Combine(candidate, "Soneta.Business.dll")));
if (installationPath is null)
{
throw new InvalidOperationException("Nie znaleziono lokalnej instalacji enova365 do testu adaptera.");
@@ -188,7 +195,7 @@ static Task MissingProduct()
static Task MissingItems()
{
var request = new OrderRequest { CustomerCode = "Abc", Items = [] };
var request = new OrderRequest { CurrencyIso = "PLN", CustomerCode = "Abc", Items = [] };
AssertHasError(request, "co najmniej jedną");
return Task.CompletedTask;
}
@@ -214,7 +221,7 @@ static async Task PlaceOrderMapping()
AssertEqual("PO/1", request.CustomerReference, "purchase_order_number");
AssertEqual(2, request.Items.Count, "liczba pozycji");
AssertEqual(12.5m, request.Items[0].Discount, "rabat");
AssertEqual(120m, request.Items[0].UnitPrice, "cena netto");
AssertEqual(120m, request.Items[0].UnitPriceNet, "cena netto");
}
static async Task PlaceOrderKeywordResult()
@@ -259,7 +266,7 @@ static async Task MissingCurrency()
static async Task UnsupportedCurrency()
{
var handler = new PlaceOrderHandler(new FakeAdapter(), CreateConfiguration());
var invocation = new RpcInvocation([], new Dictionary<string, object?> { ["currency_iso"] = "EUR" });
var invocation = CreateInvocation("EUR");
await AssertWampError(
handler.HandleAsync(invocation, CancellationToken.None),
"eu.smartb2b.erp.currency_not_supported");
@@ -376,14 +383,14 @@ static Task PartialCredentialsDisablePlaceOrder()
static Task DebugSwitchEnablesVerboseLogging()
{
var options = CommandLineOptions.Parse(["/debug"]);
var options = ServiceCommandLine.Parse(["/debug"], "enova.json");
AssertEqual(true, options.VerboseLogging, "tryb szczegółowy");
return Task.CompletedTask;
}
static Task VerboseSwitchCanBeDisabled()
{
var options = CommandLineOptions.Parse(["/verbose", "/noverbose"]);
var options = ServiceCommandLine.Parse(["/verbose", "/noverbose"], "enova.json");
AssertEqual(false, options.VerboseLogging, "wyłączony tryb szczegółowy");
return Task.CompletedTask;
}
@@ -424,11 +431,11 @@ static RuntimeSettings LoadRuntimeSettings(string credentialsJson, bool verboseL
}
}
static RpcInvocation CreateInvocation() => new(
static RpcInvocation CreateInvocation(string currency = "PLN") => new(
[],
new Dictionary<string, object?>
{
["currency_iso"] = "PLN",
["currency_iso"] = currency,
["companyErpId"] = "ABC",
["warehouseErpId"] = "MAG",
["purchase_order_number"] = "PO/1",
@@ -440,8 +447,8 @@ static RpcInvocation CreateInvocation() => new(
}
});
static EnovaAdapterConfiguration CreateConfiguration() => new(
new EnovaConnectionOptions(
static ErpAdapterConfiguration CreateConfiguration() => new(
new ErpConnectionOptions(
"Firma demo",
"Administrator",
"",
@@ -457,6 +464,7 @@ static OrderRequest CreateValidRequest(
string productCode = "BIKINI") =>
new()
{
CurrencyIso = "PLN",
CustomerCode = "Abc",
CustomerReference = "KLIENT/TEST/1",
Notes = "Test",
@@ -468,7 +476,7 @@ static OrderRequest CreateValidRequest(
{
ProductCode = productCode,
Quantity = quantity,
UnitPrice = unitPrice,
UnitPriceNet = unitPrice,
Discount = discount
}
]
@@ -514,15 +522,24 @@ static void AssertEqual(object? expected, object? actual, string name)
}
}
file sealed class FakeAdapter : IEnovaOrderAdapter
file sealed class FakeAdapter : IErpOrderAdapter
{
public OrderRequest? LastRequest { get; private set; }
public EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration) =>
new("1.0.0", "2604.4.4.0", "2604.4.4.0", configuration.Connection.DatabaseName);
public ErpAdapterInfo Initialize(ErpAdapterConfiguration configuration) =>
new("1.0.0", "enova365", "2604.4.4.0", configuration.Connection.DatabaseName, ["PLN"]);
public void CheckConnection() { }
public void Dispose() { }
public OrderResult CreateOrder(OrderRequest request)
{
if (!string.Equals(request.CurrencyIso, "PLN", StringComparison.OrdinalIgnoreCase))
{
throw new ErpOperationException("eu.smartb2b.erp.currency_not_supported", "Waluta nie jest obsługiwana.");
}
LastRequest = request;
return new OrderResult(
1,
@@ -539,13 +556,17 @@ file sealed class FakeAdapter : IEnovaOrderAdapter
}
}
file sealed class FailingAdapter : IEnovaOrderAdapter
file sealed class FailingAdapter : IErpOrderAdapter
{
public EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration) =>
new("1.0.0", "2604.4.4.0", "2604.4.4.0", configuration.Connection.DatabaseName);
public ErpAdapterInfo Initialize(ErpAdapterConfiguration configuration) =>
new("1.0.0", "enova365", "2604.4.4.0", configuration.Connection.DatabaseName, ["PLN"]);
public void CheckConnection() { }
public void Dispose() { }
public OrderResult CreateOrder(OrderRequest request) =>
throw new EnovaOperationException(
throw new ErpOperationException(
"eu.smartb2b.company_not_found",
$"Nie znaleziono kontrahenta o kodzie '{request.CustomerCode}'.");
}

View File

@@ -7,7 +7,8 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../src/SmartB2B.Enova.Adapter/SmartB2B.Enova.Adapter.csproj" />
<ProjectReference Include="../../src/SmartB2B.Enova.Contracts/SmartB2B.Enova.Contracts.csproj" />
<ProjectReference Include="../../src/SmartB2B.Sync.Contracts/SmartB2B.Sync.Contracts.csproj" />
<ProjectReference Include="../../src/SmartB2B.Sync.Service/SmartB2B.Sync.Service.csproj" />
<ProjectReference Include="../../src/SmartB2B.Enova.Service/SmartB2B.Enova.Service.csproj" />
</ItemGroup>
</Project>