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 { ["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 { ["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 { ["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 { ["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 { ["companyErpId"] = request.CustomerCode }); var warehouse = string.IsNullOrWhiteSpace(request.WarehouseCode) ? warehouses.StandardowyMagazyn : warehouses.GetGrantedView() .Cast() .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 { ["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 { ["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(); 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"; }