Register Enova database from SQL connection string

This commit is contained in:
2026-08-31 19:56:59 +00:00
parent 52c9d9b9e2
commit ee62a6253d
11 changed files with 48 additions and 17 deletions

View File

@@ -13,9 +13,9 @@ Dedykowana usługa Windows zastępująca proces Node na stanowiskach z enova365.
## Konfiguracja ## Konfiguracja
Plik `config/enova.json` zawiera portal WAMP, ścieżkę instalacji, alias bazy, operatora wraz z hasłem, connection string SQL, definicję dokumentu, magazyn domyślny, tryb zapisu oraz DSN Sentry. Repozytorium oraz paczka zawierają tylko `config/enova.json.sample`. Skrypt instalacyjny kopiuje wzorzec do `config/enova.json` wyłącznie wtedy, gdy lokalna konfiguracja jeszcze nie istnieje. Plik `config/enova.json` zawiera portal WAMP, ścieżkę instalacji, alias bazy używany wewnątrz procesu, operatora wraz z hasłem, connection string SQL, definicję dokumentu, magazyn domyślny, tryb zapisu oraz DSN Sentry. Repozytorium oraz paczka zawierają tylko `config/enova.json.sample`. Skrypt instalacyjny kopiuje wzorzec do `config/enova.json` wyłącznie wtedy, gdy lokalna konfiguracja jeszcze nie istnieje.
Uzupełnij pola `enova.operator`, `enova.password` i `sql.connectionString` bezpośrednio w lokalnym pliku `config/enova.json`. Parametr `sentry.dsn` wskazuje projekt Sentry; pusty lub pominięty DSN wyłącza wysyłanie błędów. Jeżeli operator lub hasło Enovy nie są podane, usługa nie loguje się do bazy Enovy i nie rejestruje endpointu `eu.smartb2b.place_order`; endpoint SQL oraz diagnostyka pozostają dostępne. Po zmianie konfiguracji trzeba zrestartować usługę. Connection string powinien używać konta SQL o możliwie najmniejszych uprawnieniach; `sql_raw` celowo dopuszcza również polecenia modyfikujące dane dla zgodności z obecną usługą. Uzupełnij pola `enova.operator`, `enova.password` i `sql.connectionString` bezpośrednio w lokalnym pliku `config/enova.json`. Adapter rejestruje bazę Enovy tylko w pamięci procesu na podstawie `sql.connectionString`; baza nie musi być wcześniej dodana do listy baz w profilu użytkownika Windows, a dane połączenia nie są przez usługę zapisywane w plikach Enovy. Pole `enova.database` jest nazwą tego wpisu w procesie i może odpowiadać nazwie bazy z connection stringa. Parametr `sentry.dsn` wskazuje projekt Sentry; pusty lub pominięty DSN wyłącza wysyłanie błędów. Jeżeli operator lub hasło Enovy nie są podane, usługa nie loguje się do bazy Enovy i nie rejestruje endpointu `eu.smartb2b.place_order`; endpoint SQL oraz diagnostyka pozostają dostępne. Po zmianie konfiguracji trzeba zrestartować usługę. Konto z `sql.connectionString` musi mieć uprawnienia wymagane do pracy Enovy oraz do zapytań `sql_raw`; `sql_raw` celowo dopuszcza również polecenia modyfikujące dane dla zgodności z obecną usługą.
## Budowanie i testy ## Budowanie i testy
@@ -46,7 +46,7 @@ Skrypt odtwarza katalog `release` z hostem, adapterem, wzorcem konfiguracji i Wi
1. Skopiuj katalog `release` na komputer produkcyjny. Możesz skopiować `release/config/enova.json.sample` do `release/config/enova.json` i uzupełnić go przed instalacją; jeżeli tego nie zrobisz, `install.cmd` utworzy plik automatycznie. 1. Skopiuj katalog `release` na komputer produkcyjny. Możesz skopiować `release/config/enova.json.sample` do `release/config/enova.json` i uzupełnić go przed instalacją; jeżeli tego nie zrobisz, `install.cmd` utworzy plik automatycznie.
2. Uzupełnij w `release/config/enova.json` hasło operatora i connection string SQL. 2. Uzupełnij w `release/config/enova.json` hasło operatora i connection string SQL.
3. Zatrzymaj starą usługę Node dla tego samego portalu WAMP. 3. Zatrzymaj starą usługę Node dla tego samego portalu WAMP.
4. Skonfiguruj usługę do pracy pod dedykowanym kontem Windows, które potrafi uruchomić Enovę, widzi jej bazę i licencję. 4. Skonfiguruj usługę do pracy pod dedykowanym kontem Windows, które potrafi uruchomić Enovę i ma dostęp do jej licencji. Lista baz zapisana w profilu tego konta nie jest wymagana.
5. Jako administrator uruchom `release/daemon/install.cmd`. 5. Jako administrator uruchom `release/daemon/install.cmd`.
Jeżeli usługa ma działać pod innym kontem niż `LocalSystem`, po instalacji ustaw je w `services.msc` albo poleceniem administracyjnym `sc.exe config smartb2bsync-enova obj= "DOMENA\\Użytkownik" password= "..."`, a następnie uruchom usługę ponownie. Jeżeli usługa ma działać pod innym kontem niż `LocalSystem`, po instalacji ustaw je w `services.msc` albo poleceniem administracyjnym `sc.exe config smartb2bsync-enova obj= "DOMENA\\Użytkownik" password= "..."`, a następnie uruchom usługę ponownie.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -18,18 +18,14 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
private static readonly object RuntimeLock = new(); private static readonly object RuntimeLock = new();
private static bool _runtimeLoaded; private static bool _runtimeLoaded;
private EnovaAdapterConfiguration? _configuration; private EnovaAdapterConfiguration? _configuration;
private Database? _database;
public EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration) public EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration)
{ {
ValidateConfiguration(configuration); ValidateConfiguration(configuration);
EnsureRuntimeLoaded(); EnsureRuntimeLoaded();
_ = BusApplication.Instance[configuration.Connection.DatabaseName] _database = RegisterDatabase(configuration.Connection);
?? 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; _configuration = configuration;
return new EnovaAdapterInfo( return new EnovaAdapterInfo(
@@ -43,6 +39,8 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
{ {
var configuration = _configuration var configuration = _configuration
?? throw new InvalidOperationException("Adapter Enovy nie został zainicjalizowany."); ?? throw new InvalidOperationException("Adapter Enovy nie został zainicjalizowany.");
var database = _database
?? throw new InvalidOperationException("Połączenie z bazą Enovy nie zostało zainicjalizowane.");
var errors = OrderValidator.Validate(request); var errors = OrderValidator.Validate(request);
if (errors.Count > 0) if (errors.Count > 0)
@@ -58,11 +56,6 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
var sessionState = SessionState.Create(); var sessionState = SessionState.Create();
using var attachedSessionState = sessionState.Attach(); 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( using var login = database.Login(
winAuth: false, winAuth: false,
user: configuration.Connection.OperatorName, user: configuration.Connection.OperatorName,
@@ -225,6 +218,26 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
} }
} }
private static Database RegisterDatabase(EnovaConnectionOptions connection)
{
var application = BusApplication.Instance;
var existing = application[connection.DatabaseName, throwError: false];
if (existing is not null)
{
application.Remove(existing, save: false);
}
var database = new MsSqlDatabase
{
Name = connection.DatabaseName,
UseConnectionString = true,
ConnectionString = connection.SqlConnectionString
};
application.Add(database, save: false);
return database;
}
private static void ValidateConfiguration(EnovaAdapterConfiguration configuration) private static void ValidateConfiguration(EnovaAdapterConfiguration configuration)
{ {
var errors = new List<string>(); var errors = new List<string>();
@@ -238,6 +251,11 @@ public sealed class EnovaOrderAdapter : IEnovaOrderAdapter
errors.Add("Nazwa operatora enova365 jest wymagana."); errors.Add("Nazwa operatora enova365 jest wymagana.");
} }
if (string.IsNullOrWhiteSpace(configuration.Connection.SqlConnectionString))
{
errors.Add("Connection string SQL dla bazy Enovy jest wymagany.");
}
if (string.IsNullOrWhiteSpace(configuration.DocumentDefinition)) if (string.IsNullOrWhiteSpace(configuration.DocumentDefinition))
{ {
errors.Add("Definicja dokumentu jest wymagana."); errors.Add("Definicja dokumentu jest wymagana.");

View File

@@ -9,7 +9,8 @@ public enum OrderSaveMode
public sealed record EnovaConnectionOptions( public sealed record EnovaConnectionOptions(
string DatabaseName, string DatabaseName,
string OperatorName, string OperatorName,
string Password); string Password,
string SqlConnectionString);
public sealed record EnovaAdapterConfiguration( public sealed record EnovaAdapterConfiguration(
EnovaConnectionOptions Connection, EnovaConnectionOptions Connection,

View File

@@ -57,7 +57,11 @@ public sealed class ServiceSettings
if (!string.IsNullOrWhiteSpace(Enova.Operator) && Enova.Password is not null) if (!string.IsNullOrWhiteSpace(Enova.Operator) && Enova.Password is not null)
{ {
enovaConfiguration = new EnovaAdapterConfiguration( enovaConfiguration = new EnovaAdapterConfiguration(
new EnovaConnectionOptions(Enova.Database, Enova.Operator, Enova.Password), new EnovaConnectionOptions(
Enova.Database,
Enova.Operator,
Enova.Password,
Sql.ConnectionString),
Enova.DocumentDefinition, Enova.DocumentDefinition,
Enova.DefaultWarehouseCode, Enova.DefaultWarehouseCode,
Enova.SaveMode); Enova.SaveMode);

View File

@@ -255,6 +255,10 @@ static Task SecretsComeFromConfigurationFile()
"Server=db;Database=enova;User ID=user;Password=sql-z-pliku", "Server=db;Database=enova;User ID=user;Password=sql-z-pliku",
runtime.SqlConnectionString, runtime.SqlConnectionString,
"connection string SQL"); "connection string SQL");
AssertEqual(
runtime.SqlConnectionString,
runtime.EnovaConfiguration?.Connection.SqlConnectionString,
"connection string rejestracji bazy Enovy");
return Task.CompletedTask; return Task.CompletedTask;
} }
finally finally
@@ -321,7 +325,11 @@ static RpcInvocation CreateInvocation() => new(
}); });
static EnovaAdapterConfiguration CreateConfiguration() => new( static EnovaAdapterConfiguration CreateConfiguration() => new(
new EnovaConnectionOptions("Firma demo", "Administrator", ""), new EnovaConnectionOptions(
"Firma demo",
"Administrator",
"",
"Server=localhost;Database=enova;Integrated Security=true;TrustServerCertificate=true"),
"ZO", "ZO",
null, null,
OrderSaveMode.Buffer); OrderSaveMode.Buffer);