using MeterVault.Core.Domain; namespace MeterVault.Infrastructure.Dashboard; /// A raw reading row for the meter-detail table. public sealed record ReadingRow(DateTimeOffset Time, double Value, ReadingQuality Quality, ReadingFlags Flags); /// A normalized consumption row for the meter-detail table. public sealed record ConsumptionDetailRow(DateTimeOffset Time, double Amount, ConsumptionKind Kind, ReadingQuality Quality); /// One calendar month of a meter's normalized history, bucketed in the instance timezone. public sealed record MeterMonthPoint(DateOnly Month, double Amount, double Cost); /// /// A meter framed the way it is actually read: what it used this period, how that compares with the /// last one, and where the year is heading. Amounts are generation for a generation counter and /// consumption otherwise, so says which — as the enum, not a word, because the /// wording belongs to whichever language the reader picked. /// /// /// Month- and year-to-date are compared against a projection of the current period rather /// than its raw running total: three days into a month, "12 kWh vs 340 kWh last month" reads as a /// collapse in usage when nothing has changed. Projections are flagged so the UI can mark them. /// public sealed record MeterPeriodView( ConsumptionKind Kind, string Unit, string Currency, double MonthToDate, double MonthProjected, double LastMonth, double YearToDate, double YearProjected, double LastYear, double YearToDateCost, double YearProjectedCost, double LastYearCost, bool MonthIsPartial, IReadOnlyList Last12Months) { /// Projected month against last month, as a fraction (+0.12 = 12% more). Null if no basis. public double? MonthChange => Ratio(MonthProjected, LastMonth); /// Projected year against last year, as a fraction. Null if no basis. public double? YearChange => Ratio(YearProjected, LastYear); public bool HasHistory => Last12Months.Count > 0; /// /// Percentage change is only meaningful against a positive baseline. Dividing by a negative one /// inverts the sign — a net-export meter going from −100 to −150 would report "+50% more used" /// when it exported half as much again — so those report no basis rather than a confident lie. /// private static double? Ratio(double current, double previous) => previous <= 1e-9 ? null : (current - previous) / previous; } /// A meter lifecycle/correction event row. public sealed record EventRow(DateTimeOffset Time, MeterEventType Type, double? Amount, double? PrevValue, double? NewValue, string? Unit, string? Notes); /// A tariff applicable to the meter (own / energy-type / global scope), for the timeline. public sealed record TariffRow(TariffScope Scope, int? ScopeId, TariffComponent Component, double Value, string Unit, DateOnly ValidFrom, DateOnly? ValidTo); /// /// The meter-detail read model (SDD §8.6): identity, register span, totals, recent raw readings /// and normalized consumption (measured-vs-estimated markers via quality), the applicable tariff /// timeline, and lifecycle events (swaps/deliveries/corrections). Source management loads the /// source entities directly (they are editable), so it is not part of this read model. /// public sealed record MeterDetailView( int Id, string Name, string EnergyType, MeterMode Mode, string Unit, string? Location, string? SerialNumber, string? Manufacturer, string? Model, double InitialBaseline, bool IsActive, int ReadingCount, int ConsumptionCount, DateTimeOffset? FirstReadingTime, DateTimeOffset? LastReadingTime, double? FirstReadingValue, double? LastReadingValue, double TotalConsumption, double TotalGeneration, IReadOnlyList RecentReadings, IReadOnlyList RecentConsumption, IReadOnlyList Events, IReadOnlyList Tariffs, int SourceCount);