using Distribution.Infra.UoW; using Distribution.Models; namespace Distribution.Features.SysConfig; public class SysConfigService(ISysConfigRepository repo, IUnitOfWork uow) { public const string KeyInvoiceCreationMode = "InvoiceCreationMode"; public const string KeySerialNumberTrackingEnabled = "Feature.SerialNumberTracking"; public const string KeyBatchTrackingEnabled = "Feature.BatchTracking"; public const string KeyExpiryDateTrackingEnabled = "Feature.ExpiryDateTracking"; public const string KeyWarrantyTrackingEnabled = "Feature.WarrantyTracking"; public const string KeyCustomWarrantyOverrideEnabled = "Feature.CustomWarrantyOverride"; public const string KeyDefaultWarrantyPeriodDays = "Feature.DefaultWarrantyPeriodDays"; private static readonly string[] BooleanFeatureKeys = [ KeySerialNumberTrackingEnabled, KeyBatchTrackingEnabled, KeyExpiryDateTrackingEnabled, KeyWarrantyTrackingEnabled, KeyCustomWarrantyOverrideEnabled, ]; public async Task> GetAllAsync() { var configs = await repo.GetAllAsync(); return configs.Select(ToResponse).ToList(); } public async Task GetByKeyAsync(string key) { var config = await repo.GetByKeyAsync(key); return config is null ? null : ToResponse(config); } public async Task SetAsync(string key, string value, Guid userId, CancellationToken ct = default) { ValidateValue(key, value); var config = new Models.SysConfig { Key = key, Value = value, UpdatedAt = DateTime.Now, UpdatedBy = userId }; await uow.BeginAsync(ct); try { await repo.UpsertAsync(config); await uow.CommitAsync(ct); } catch { await uow.RollbackAsync(ct); throw; } var saved = await repo.GetByKeyAsync(key); return ToResponse(saved!); } /// /// Returns the current InvoiceCreationMode, defaulting to ModeB if not configured. /// ModeA = invoice auto-created on ShopPO confirmation. /// ModeB = invoice created as a separate explicit step. /// public async Task GetInvoiceCreationModeAsync() { var config = await repo.GetByKeyAsync(KeyInvoiceCreationMode); if (config is null) return InvoiceCreationMode.ModeB; return Enum.TryParse(config.Value, out var mode) ? mode : InvoiceCreationMode.ModeB; } public async Task GetSerialNumberTrackingEnabledAsync() => await GetBoolAsync(KeySerialNumberTrackingEnabled); public async Task GetBatchTrackingEnabledAsync() => await GetBoolAsync(KeyBatchTrackingEnabled); public async Task GetExpiryDateTrackingEnabledAsync() => await GetBoolAsync(KeyExpiryDateTrackingEnabled); public async Task GetWarrantyTrackingEnabledAsync() => await GetBoolAsync(KeyWarrantyTrackingEnabled); public async Task GetCustomWarrantyOverrideEnabledAsync() => await GetBoolAsync(KeyCustomWarrantyOverrideEnabled); public async Task GetDefaultWarrantyPeriodDaysAsync() { var config = await repo.GetByKeyAsync(KeyDefaultWarrantyPeriodDays); return config is not null && int.TryParse(config.Value, out var days) ? days : 0; } private async Task GetBoolAsync(string key) { var config = await repo.GetByKeyAsync(key); return config is not null && bool.TryParse(config.Value, out var enabled) && enabled; } private static void ValidateValue(string key, string value) { if (key == KeyInvoiceCreationMode && !Enum.TryParse(value, out _)) throw new InvalidOperationException( $"Invalid value '{value}' for '{key}'. Allowed values: {string.Join(", ", Enum.GetNames())}."); if (BooleanFeatureKeys.Contains(key) && !bool.TryParse(value, out _)) throw new InvalidOperationException($"Invalid value '{value}' for '{key}'. Allowed values: true, false."); if (key == KeyDefaultWarrantyPeriodDays && (!int.TryParse(value, out var days) || days < 0)) throw new InvalidOperationException($"Invalid value '{value}' for '{key}'. Must be a non-negative integer."); } private static SysConfigResponse ToResponse(Models.SysConfig c) => new(c.Key, c.Value, c.UpdatedAt, c.UpdatedBy); }