257 lines
9.3 KiB
C#
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";
|
|
}
|