@page "/admin/tariffs" @inject Microsoft.EntityFrameworkCore.IDbContextFactory DbFactory @inject ISnackbar Snackbar @inject IDialogService DialogService @using Microsoft.EntityFrameworkCore @using MudBlazor MeterVault — Tariffs
Tariffs Add tariff
@if (_tariffs is null) { } else { Scope Component Value Unit Valid from Valid to Actions @ScopeLabel(context) @context.Component @Format.Number(context.Value, 4) @context.Unit @context.ValidFrom.ToString("yyyy-MM-dd") @(context.ValidTo?.ToString("yyyy-MM-dd") ?? "open") @if (_tariffs.Count == 0) { No tariffs yet. Add one, or load the reference data from Import. } } @(_working.Id == 0 ? "New tariff" : "Edit tariff") @foreach (var scope in Enum.GetValues()) { @scope } @if (_working.ScopeType == TariffScope.EnergyType) { @foreach (var t in _energyTypes) { @t.DisplayName } } else if (_working.ScopeType == TariffScope.Meter) { @foreach (var m in _meters) { @m.Name } } @foreach (var component in Enum.GetValues()) { @component } Cancel Save @code { private List? _tariffs; private List _energyTypes = []; private List _meters = []; private bool _editOpen; private EditModel _working = new(); private readonly DialogOptions _dialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true }; protected override Task OnInitializedAsync() => LoadAsync(); private async Task LoadAsync() { await using var db = await DbFactory.CreateDbContextAsync(); _tariffs = await db.Tariffs.AsNoTracking().OrderBy(t => t.Component).ThenBy(t => t.ValidFrom).ToListAsync(); _energyTypes = await db.EnergyTypes.AsNoTracking().OrderBy(t => t.DisplayName).ToListAsync(); _meters = await db.Meters.AsNoTracking().OrderBy(m => m.Name).ToListAsync(); } private string ScopeLabel(Tariff t) => t.ScopeType switch { TariffScope.Global => "Global", TariffScope.EnergyType => $"Type: {_energyTypes.FirstOrDefault(x => x.Id == t.ScopeId)?.DisplayName ?? $"#{t.ScopeId}"}", TariffScope.Meter => $"Meter: {_meters.FirstOrDefault(x => x.Id == t.ScopeId)?.Name ?? $"#{t.ScopeId}"}", _ => t.ScopeType.ToString(), }; private void OpenEdit(Tariff? tariff) { _working = tariff is null ? new EditModel { ValidFrom = DateTime.Today } : new EditModel { Id = tariff.Id, ScopeType = tariff.ScopeType, ScopeId = tariff.ScopeId, Component = tariff.Component, Value = tariff.Value, Unit = tariff.Unit, Currency = tariff.Currency, ValidFrom = tariff.ValidFrom.ToDateTime(TimeOnly.MinValue), ValidTo = tariff.ValidTo?.ToDateTime(TimeOnly.MinValue), Notes = tariff.Notes, }; _editOpen = true; } private async Task SaveAsync() { if (string.IsNullOrWhiteSpace(_working.Unit) || _working.ValidFrom is null) { Snackbar.Add("Unit and valid-from are required.", Severity.Warning); return; } if (_working.ScopeType != TariffScope.Global && _working.ScopeId is null) { Snackbar.Add("Select the energy type or meter this tariff applies to.", Severity.Warning); return; } var scopeId = _working.ScopeType == TariffScope.Global ? null : _working.ScopeId; await using var db = await DbFactory.CreateDbContextAsync(); if (_working.Id == 0) { db.Tariffs.Add(new Tariff { ScopeType = _working.ScopeType, ScopeId = scopeId, Component = _working.Component, Value = _working.Value, Unit = _working.Unit.Trim(), Currency = string.IsNullOrWhiteSpace(_working.Currency) ? "EUR" : _working.Currency.Trim(), ValidFrom = DateOnly.FromDateTime(_working.ValidFrom.Value), ValidTo = _working.ValidTo is { } to ? DateOnly.FromDateTime(to) : null, Notes = string.IsNullOrWhiteSpace(_working.Notes) ? null : _working.Notes, }); } else { var existing = await db.Tariffs.FirstAsync(t => t.Id == _working.Id); existing.ScopeType = _working.ScopeType; existing.ScopeId = scopeId; existing.Component = _working.Component; existing.Value = _working.Value; existing.Unit = _working.Unit.Trim(); existing.Currency = string.IsNullOrWhiteSpace(_working.Currency) ? "EUR" : _working.Currency.Trim(); existing.ValidFrom = DateOnly.FromDateTime(_working.ValidFrom.Value); existing.ValidTo = _working.ValidTo is { } to ? DateOnly.FromDateTime(to) : null; existing.Notes = string.IsNullOrWhiteSpace(_working.Notes) ? null : _working.Notes; } await db.SaveChangesAsync(); _editOpen = false; Snackbar.Add("Saved.", Severity.Success); await LoadAsync(); } private async Task DeleteAsync(Tariff tariff) { if (!await Confirm.DeleteAsync(DialogService, "Delete tariff", $"Delete this {tariff.Component} tariff ({Format.Number(tariff.Value, 4)} {tariff.Unit})?")) { return; } await using var db = await DbFactory.CreateDbContextAsync(); var target = await db.Tariffs.FirstOrDefaultAsync(t => t.Id == tariff.Id); if (target is not null) { db.Tariffs.Remove(target); await db.SaveChangesAsync(); Snackbar.Add("Deleted.", Severity.Success); } await LoadAsync(); } private sealed class EditModel { public int Id { get; set; } public TariffScope ScopeType { get; set; } = TariffScope.EnergyType; public int? ScopeId { get; set; } public TariffComponent Component { get; set; } = TariffComponent.UnitPrice; public double Value { get; set; } public string Unit { get; set; } = "EUR/kWh"; public string Currency { get; set; } = "EUR"; public DateTime? ValidFrom { get; set; } public DateTime? ValidTo { get; set; } public string? Notes { get; set; } } }