Files
Sync-Enova365/src/SmartB2B.Enova.Adapter/EnovaOrderAdapter.cs

257 lines
9.3 KiB
C#

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";
}