using MeterVault.Core.Domain; using MeterVault.Core.Normalization; using MeterVault.Infrastructure.Import; using static MeterVault.Integration.Tests.Reconciliation.ReconciliationSupport; namespace MeterVault.Integration.Tests.Reconciliation; /// /// Reconciles the heating-oil tank against the Heizöl sheet (SDD §2.4). The sheet's /// Differenz Tank (col 9) is the negated actual consumption, already netting deliveries — /// exactly what the consumable-balance normalizer computes (prevLevel + deliveries − currLevel). /// The burner runtime meter's Δhours is reconciled against Differenz Betrieb (col 2). /// public sealed class OilReconciliationTests { [Fact] public void Tank_consumption_matches_differenz_tank_including_deliveries() { var staged = Stage(ReferenceProfiles.HeatingOil(), Oil); var config = new MeterConfig { MeterId = ReferenceProfiles.OilTank, Mode = MeterMode.ConsumableBalance, Unit = "L", Tank = new TankConfig { Capacity = 7000, Calibration = new CalibrationCurve(ReferenceProfiles.OilLitresPerCm), }, }; var computed = ByDate(Normalize(staged, config)); // Differenz Tank is negative consumption → negate to compare with positive draw. var oracle = OracleByDate(ReadRows(Oil), dateColumn: 0, valueColumn: 9, firstDataRow: 4, v => -v); // ±2 L covers cm→litre rounding on both ends of each interval. AssertReconciles(computed, oracle, tolerance: 2.0, "oil tank", minMatches: 30); } [Fact] public void Burner_delta_hours_match_differenz_betrieb() { var staged = Stage(ReferenceProfiles.HeatingOil(), Oil); var config = new MeterConfig { MeterId = ReferenceProfiles.Burner, Mode = MeterMode.RuntimeCounter, Unit = "h", }; var computed = ByDate(Normalize(staged, config)); var oracle = OracleByDate(ReadRows(Oil), dateColumn: 0, valueColumn: 2, firstDataRow: 4); AssertReconciles(computed, oracle, tolerance: 0.5, "burner hours", minMatches: 30); } }