Initial Enova365 sync service
This commit is contained in:
256
src/SmartB2B.Enova.Adapter/EnovaOrderAdapter.cs
Normal file
256
src/SmartB2B.Enova.Adapter/EnovaOrderAdapter.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using System.Reflection;
|
||||
using SmartB2B.Enova.Contracts;
|
||||
using Soneta.Business;
|
||||
using Soneta.Business.App;
|
||||
using Soneta.CRM;
|
||||
using Soneta.Handel;
|
||||
using Soneta.Magazyny;
|
||||
using Soneta.Start;
|
||||
using Soneta.Tools;
|
||||
using Soneta.Towary;
|
||||
using Soneta.Types;
|
||||
|
||||
namespace SmartB2B.Enova.Adapter;
|
||||
|
||||
public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
|
||||
{
|
||||
private const string CurrencySymbol = "PLN";
|
||||
private static readonly object RuntimeLock = new();
|
||||
private static bool _runtimeLoaded;
|
||||
private EnovaAdapterConfiguration? _configuration;
|
||||
|
||||
public EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration)
|
||||
{
|
||||
ValidateConfiguration(configuration);
|
||||
EnsureRuntimeLoaded();
|
||||
|
||||
_ = BusApplication.Instance[configuration.Connection.DatabaseName]
|
||||
?? throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.database_not_found",
|
||||
$"Nie znaleziono zarejestrowanej bazy enova365 '{configuration.Connection.DatabaseName}'.",
|
||||
new Dictionary<string, object?> { ["database"] = configuration.Connection.DatabaseName });
|
||||
|
||||
_configuration = configuration;
|
||||
|
||||
return new EnovaAdapterInfo(
|
||||
GetAssemblyVersion(typeof(EnovaOrderAdapter).Assembly),
|
||||
GetAssemblyVersion(typeof(Session).Assembly),
|
||||
GetAssemblyVersion(typeof(HandelModule).Assembly),
|
||||
configuration.Connection.DatabaseName);
|
||||
}
|
||||
|
||||
public OrderResult CreateOrder(OrderRequest request)
|
||||
{
|
||||
var configuration = _configuration
|
||||
?? throw new InvalidOperationException("Adapter Enovy nie został zainicjalizowany.");
|
||||
|
||||
var errors = OrderValidator.Validate(request);
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.invalid_order_lines",
|
||||
string.Join(" | ", errors),
|
||||
new Dictionary<string, object?> { ["errors"] = errors });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var sessionState = SessionState.Create();
|
||||
using var attachedSessionState = sessionState.Attach();
|
||||
|
||||
var database = BusApplication.Instance[configuration.Connection.DatabaseName]
|
||||
?? throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.database_not_found",
|
||||
$"Nie znaleziono zarejestrowanej bazy enova365 '{configuration.Connection.DatabaseName}'.");
|
||||
|
||||
using var login = database.Login(
|
||||
winAuth: false,
|
||||
user: configuration.Connection.OperatorName,
|
||||
password: configuration.Connection.Password);
|
||||
using var session = login.CreateSession(
|
||||
readOnly: false,
|
||||
config: false,
|
||||
name: "SmartB2B Enova Sync");
|
||||
|
||||
return CreateOrderInSession(session, request);
|
||||
}
|
||||
catch (EnovaOperationException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.error",
|
||||
$"Nie udało się utworzyć zamówienia w bazie '{configuration.Connection.DatabaseName}': {exception.Message}",
|
||||
new Dictionary<string, object?> { ["message"] = exception.Message },
|
||||
exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static OrderResult CreateOrderInSession(Session session, OrderRequest request)
|
||||
{
|
||||
var handel = HandelModule.GetInstance(session);
|
||||
var crm = CRMModule.GetInstance(session);
|
||||
var towary = TowaryModule.GetInstance(session);
|
||||
var warehouses = MagazynyModule.GetInstance(session).Magazyny;
|
||||
|
||||
var definition = handel.DefDokHandlowych.WgSymbolu[request.DocumentDefinition]
|
||||
?? throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.document_definition_not_found",
|
||||
$"Nie znaleziono definicji dokumentu '{request.DocumentDefinition}'.",
|
||||
new Dictionary<string, object?> { ["documentDefinition"] = request.DocumentDefinition });
|
||||
var customer = crm.Kontrahenci.WgKodu[request.CustomerCode]
|
||||
?? throw new EnovaOperationException(
|
||||
"eu.smartb2b.company_not_found",
|
||||
$"Nie znaleziono kontrahenta o kodzie '{request.CustomerCode}'.",
|
||||
new Dictionary<string, object?> { ["companyErpId"] = request.CustomerCode });
|
||||
var warehouse = string.IsNullOrWhiteSpace(request.WarehouseCode)
|
||||
? warehouses.StandardowyMagazyn
|
||||
: warehouses.GetGrantedView()
|
||||
.Cast<Magazyn>()
|
||||
.FirstOrDefault(candidate => string.Equals(
|
||||
candidate.Symbol,
|
||||
request.WarehouseCode.Trim(),
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (warehouse is null)
|
||||
{
|
||||
throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.warehouse_not_found",
|
||||
string.IsNullOrWhiteSpace(request.WarehouseCode)
|
||||
? "Nie znaleziono standardowego magazynu firmy."
|
||||
: $"Nie znaleziono magazynu o symbolu '{request.WarehouseCode.Trim()}'.",
|
||||
new Dictionary<string, object?> { ["warehouseErpId"] = request.WarehouseCode });
|
||||
}
|
||||
|
||||
DokumentHandlowy document;
|
||||
|
||||
using (var transaction = session.Logout(editMode: true))
|
||||
{
|
||||
document = session.AddRow(new DokumentHandlowy());
|
||||
document.Definicja = definition;
|
||||
document.Kontrahent = customer;
|
||||
document.Magazyn = warehouse;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.CustomerReference))
|
||||
{
|
||||
document.Obcy.Numer = request.CustomerReference.Trim();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.Notes))
|
||||
{
|
||||
document.Opis.Clear();
|
||||
document.Opis.Add(request.Notes.Trim());
|
||||
}
|
||||
|
||||
foreach (var line in request.Items)
|
||||
{
|
||||
var product = towary.Towary.WgKodu[line.ProductCode]
|
||||
?? throw new EnovaOperationException(
|
||||
"eu.smartb2b.product_not_found",
|
||||
$"Nie znaleziono towaru o kodzie '{line.ProductCode}'.",
|
||||
new Dictionary<string, object?> { ["symbol"] = line.ProductCode });
|
||||
|
||||
var position = session.AddRow(new PozycjaDokHandlowego(document));
|
||||
position.Towar = product;
|
||||
position.Ilosc = new Quantity((double)line.Quantity);
|
||||
position.Cena = new DoubleCy(line.UnitPrice, CurrencySymbol);
|
||||
position.UstawRabat(new Percent(line.Discount / 100m), ręcznie: true);
|
||||
}
|
||||
|
||||
document.Stan = request.SaveMode switch
|
||||
{
|
||||
OrderSaveMode.Buffer => StanDokumentuHandlowego.Bufor,
|
||||
OrderSaveMode.Approved => StanDokumentuHandlowego.Zatwierdzony,
|
||||
_ => throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.invalid_order",
|
||||
$"Nieobsługiwany tryb zapisu: {request.SaveMode}.")
|
||||
};
|
||||
|
||||
transaction.Commit();
|
||||
}
|
||||
|
||||
session.Save();
|
||||
|
||||
return new OrderResult(
|
||||
document.ID,
|
||||
document.Numer.Pelny.ToString(),
|
||||
request.SaveMode,
|
||||
customer.Kod,
|
||||
warehouse.Symbol,
|
||||
document.Obcy.Numer,
|
||||
request.Items.Count,
|
||||
document.Suma.Netto,
|
||||
document.Suma.VAT,
|
||||
document.Suma.Brutto,
|
||||
request.Items.Any(item => item.Discount > 0));
|
||||
}
|
||||
|
||||
private static void EnsureRuntimeLoaded()
|
||||
{
|
||||
if (_runtimeLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (RuntimeLock)
|
||||
{
|
||||
if (_runtimeLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var loader = new Loader
|
||||
{
|
||||
WithUI = false,
|
||||
WithNet = false,
|
||||
WithExtra = false,
|
||||
WithExtensions = false,
|
||||
CheckWinForms = false
|
||||
};
|
||||
|
||||
loader.UseCurrentDllsAsInstallation(standard: true);
|
||||
loader.Load();
|
||||
|
||||
var errors = Loader.GetListOfErrors();
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.runtime_error",
|
||||
$"Loader enova365 zgłosił błędy: {string.Join(" | ", errors)}");
|
||||
}
|
||||
|
||||
_runtimeLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateConfiguration(EnovaAdapterConfiguration configuration)
|
||||
{
|
||||
var errors = new List<string>();
|
||||
if (string.IsNullOrWhiteSpace(configuration.Connection.DatabaseName))
|
||||
{
|
||||
errors.Add("Nazwa bazy enova365 jest wymagana.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(configuration.Connection.OperatorName))
|
||||
{
|
||||
errors.Add("Nazwa operatora enova365 jest wymagana.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(configuration.DocumentDefinition))
|
||||
{
|
||||
errors.Add("Definicja dokumentu jest wymagana.");
|
||||
}
|
||||
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
throw new EnovaOperationException(
|
||||
"eu.smartb2b.erp.configuration_error",
|
||||
string.Join(" | ", errors));
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetAssemblyVersion(Assembly assembly) =>
|
||||
assembly.GetName().Version?.ToString() ?? "unknown";
|
||||
}
|
||||
Reference in New Issue
Block a user