Initial Enova365 sync service

This commit is contained in:
2026-07-15 08:27:32 +00:00
commit 605c6b7d2a
50 changed files with 2813 additions and 0 deletions

View File

@@ -0,0 +1,154 @@
namespace SmartB2B.Enova.Contracts;
public enum OrderSaveMode
{
Buffer,
Approved
}
public sealed record EnovaConnectionOptions(
string DatabaseName,
string OperatorName,
string Password);
public sealed record EnovaAdapterConfiguration(
EnovaConnectionOptions Connection,
string DocumentDefinition,
string? DefaultWarehouseCode,
OrderSaveMode SaveMode);
public sealed class OrderRequest
{
public string CustomerCode { get; init; } = string.Empty;
public string? CustomerReference { get; init; }
public string? Notes { get; init; }
public string DocumentDefinition { get; init; } = "ZO";
public string? WarehouseCode { get; init; }
public OrderSaveMode SaveMode { get; init; } = OrderSaveMode.Buffer;
public IReadOnlyList<OrderLineRequest> Items { get; init; } = [];
}
public sealed class OrderLineRequest
{
public string ProductCode { get; init; } = string.Empty;
public decimal Quantity { get; init; }
public decimal UnitPrice { get; init; }
public decimal Discount { get; init; }
}
public sealed record OrderResult(
int Id,
string Number,
OrderSaveMode SaveMode,
string CustomerCode,
string WarehouseCode,
string? CustomerReference,
int LineCount,
decimal Net,
decimal Vat,
decimal Gross,
bool HasDiscount);
public sealed record EnovaAdapterInfo(
string AdapterVersion,
string SonetaBusinessVersion,
string SonetaHandelVersion,
string DatabaseName);
public interface IEnovaOrderAdapter
{
EnovaAdapterInfo Initialize(EnovaAdapterConfiguration configuration);
OrderResult CreateOrder(OrderRequest request);
}
public sealed class EnovaOperationException : Exception
{
public EnovaOperationException(
string errorUri,
string message,
IReadOnlyDictionary<string, object?>? details = null,
Exception? innerException = null)
: base(message, innerException)
{
ErrorUri = errorUri;
Details = details ?? new Dictionary<string, object?>();
}
public string ErrorUri { get; }
public IReadOnlyDictionary<string, object?> Details { get; }
}
public static class OrderValidator
{
public static IReadOnlyList<string> Validate(OrderRequest? request)
{
var errors = new List<string>();
if (request is null)
{
errors.Add("Brak danych zamówienia.");
return errors;
}
if (string.IsNullOrWhiteSpace(request.CustomerCode))
{
errors.Add("Pole companyErpId jest wymagane.");
}
if (string.IsNullOrWhiteSpace(request.DocumentDefinition))
{
errors.Add("Definicja dokumentu jest wymagana.");
}
if (request.Items is null || request.Items.Count == 0)
{
errors.Add("Zamówienie musi zawierać co najmniej jedną pozycję.");
return errors;
}
for (var index = 0; index < request.Items.Count; index++)
{
var line = request.Items[index];
var prefix = $"lines[{index}]";
if (line is null)
{
errors.Add($"{prefix}: pozycja nie może być pusta.");
continue;
}
if (string.IsNullOrWhiteSpace(line.ProductCode))
{
errors.Add($"{prefix}.symbol jest wymagane.");
}
if (line.Quantity <= 0)
{
errors.Add($"{prefix}.quantity musi być większe od zera.");
}
if (line.UnitPrice < 0)
{
errors.Add($"{prefix}.price_netto nie może być ujemne.");
}
if (line.Discount is < 0 or > 100)
{
errors.Add($"{prefix}.discount musi mieścić się w zakresie 0-100.");
}
}
return errors;
}
}