-- 0003_bi_views.sql: the views Superset reads through Trino (schema `public`), plus -- fm.v_site_baseline_status. Owned by fuel_owner, so trino_ro can read them through the existing -- default privileges; they run with the owner's rights, so Trino never reads `fm` (it lists fm's -- table names, as it lists every schema's, but any query on them is refused). -- DRAFT, not applied. Applied after 0002 by a loader acting as fuel_owner (README in this folder). -- Design: fuel-monitoring .context/plans/2026-10-24-fuel-db-baseline-dashboard.md, §5.2 and §6. BEGIN; SET LOCAL search_path = fm, pg_catalog, public; -- Status, estimate and method come from ONE baseline_version, never from ad-hoc pooling of intervals -- (exploratory interval figures stay on the Baseline & Evidence tab): -- approved : the latest approved KPI version; a decision is recorded. The only state that says "approved". -- If its inputs changed since, it stays approved and baseline_validity_note says so. -- eligible : no approval; the latest proposed KPI version passed BP-1 when `baseline propose` -- computed it from its inputs (meets_policy), and those inputs are unchanged today. -- provisional : no approval; a proposed version below BP-1, or usable intervals but no version yet -- (then no estimate is shown). -- insufficient_evidence: none of the above (today: every site, since there is no runtime). CREATE VIEW fm.v_site_baseline_status AS WITH cur AS ( SELECT DISTINCT ON (b.site_uid) b.* FROM fm.baseline_version b WHERE b.status IN ('approved','proposed') AND b.metric IN ('measured_generator_l_per_h','apparent_l_per_generator_h', 'apparent_l_per_set_h','apparent_l_per_site_dg_h') -- never modelled, never per-day ORDER BY b.site_uid, (b.status = 'approved') DESC, b.approved_on DESC NULLS LAST, b.computed_at DESC), ev AS ( SELECT site_uid, count(*) FILTER (WHERE eligibility <> 'excluded') AS n_usable FROM fm.v_lph_interval GROUP BY site_uid) SELECT s.site_uid, CASE WHEN cur.status = 'approved' THEN 'approved' WHEN cur.status = 'proposed' AND cur.meets_policy AND val.inputs_unchanged THEN 'eligible' WHEN cur.status = 'proposed' OR coalesce(ev.n_usable, 0) > 0 THEN 'provisional' ELSE 'insufficient_evidence' END AS baseline_status, CASE WHEN cur.baseline_id IS NOT NULL AND NOT coalesce(val.inputs_unchanged, false) THEN CASE cur.status WHEN 'approved' THEN 'Inputs changed since approval: review needed' ELSE 'Inputs changed since computation: re-propose' END END AS baseline_validity_note, cur.code AS baseline_code, CASE WHEN cur.status = 'approved' OR val.inputs_unchanged THEN cur.value END AS lph_estimate, CASE WHEN cur.status = 'approved' OR val.inputs_unchanged THEN cur.range_low END AS range_low, CASE WHEN cur.status = 'approved' OR val.inputs_unchanged THEN cur.range_high END AS range_high, cur.metric AS lph_metric, cur.population, cur.window_start AS lph_window_start, cur.window_end AS lph_window_end, cur.conditions, cur.decision_ref, coalesce(ev.n_usable, 0) AS n_usable_intervals FROM fm.site s LEFT JOIN cur USING (site_uid) LEFT JOIN ev USING (site_uid) LEFT JOIN fm.v_baseline_validity val ON val.baseline_id = cur.baseline_id; -- One row per site, every status: the map dataset and the single source of site-level fields. -- "Current" means the period that contains today's date in Kabul, not "no end date". CREATE VIEW public.v_site AS WITH today AS (SELECT (now() AT TIME ZONE 'Asia/Kabul')::date AS d), alias AS ( -- v1: the operator is ATOMA; later: the site's current operator SELECT a.site_uid, a.label FROM fm.site_alias a, today WHERE a.issuer = 'ATOMA' AND a.is_primary AND daterange(coalesce(a.valid_from, '-infinity'::date), a.valid_to, '[)') @> today.d), pp AS ( -- one programme, chosen explicitly SELECT p.site_uid, p.status FROM fm.pilot_participation p, today WHERE p.programme = 'ATOMA OFMS pilot' AND daterange(p.valid_from, p.valid_to, '[)') @> today.d), -- alias and pp are at most one row per site: the exclusion constraints forbid overlapping primary -- labels per (site, issuer) and overlapping periods per (site, programme). loc AS (SELECT site_uid, latitude, longitude, basis, precision_m FROM fm.site_location WHERE acceptance = 'accepted'), loc_any AS (SELECT site_uid, bool_or(acceptance = 'pending') AS has_pending FROM fm.site_location GROUP BY site_uid), cal AS (SELECT site_uid, sum(consumed_l) AS fuel_l, sum(days) AS days FROM fm.v_stock_interval WHERE stock_status = 'ok' GROUP BY site_uid), latest AS (SELECT site_uid, max(visit_on) AS latest_record_on -- live records only: a fully FROM fm.v_visit_live GROUP BY site_uid) -- retracted visit does not count SELECT s.site_uid::varchar(12) AS site_uid, coalesce(alias.label, s.site_uid)::varchar(20) AS site_code, coalesce(s.name, 'name not recorded')::varchar(120) AS site_name, (coalesce(alias.label, s.site_uid) || ' - ' || coalesce(s.name, 'name not recorded') || ' - ' || s.site_uid)::varchar(170) AS site_label, -- the one filter value s.zone::varchar(40) AS zone, s.province::varchar(40) AS province, s.district::varchar(60) AS district, coalesce(pp.status, 'none')::varchar(12) AS pilot_status, loc.latitude::numeric(9,6) AS latitude, loc.longitude::numeric(9,6) AS longitude, -- NULL unless accepted (CASE WHEN loc.site_uid IS NOT NULL THEN 'accepted' WHEN loc_any.has_pending THEN 'awaiting acceptance' ELSE 'missing' END)::varchar(20) AS location_status, loc.basis::varchar(20) AS location_basis, loc.precision_m::integer AS location_precision_m, bs.baseline_status::varchar(24) AS baseline_status, (CASE bs.baseline_status WHEN 'approved' THEN 'Approved baseline' WHEN 'eligible' THEN 'Eligible - not yet approved' WHEN 'provisional' THEN 'Provisional' ELSE 'Insufficient evidence' END)::varchar(40) AS baseline_status_label, bs.baseline_validity_note::varchar(60) AS baseline_validity_note, round(bs.lph_estimate, 2)::numeric(6,2) AS lph_estimate, (CASE bs.lph_metric WHEN 'apparent_l_per_generator_h' THEN 'Stock balance per generator running hour' WHEN 'apparent_l_per_set_h' THEN 'Stock balance per summed generator-hour' WHEN 'apparent_l_per_site_dg_h' THEN 'Stock balance per hour with any generator running' WHEN 'measured_generator_l_per_h' THEN 'Measured engine fuel per running hour' ELSE CASE WHEN bs.n_usable_intervals > 0 THEN 'Not available: no baseline version proposed' ELSE 'Not available: no runtime evidence' END END)::varchar(60) AS lph_method, bs.lph_window_start, bs.lph_window_end, bs.conditions::varchar(200) AS baseline_conditions, bs.decision_ref::varchar(12) AS baseline_decision_ref, round(cal.fuel_l / NULLIF(cal.days, 0), 1)::numeric(7,1) AS fuel_l_per_calendar_day, latest.latest_record_on FROM fm.site s LEFT JOIN alias USING (site_uid) LEFT JOIN loc USING (site_uid) LEFT JOIN loc_any USING (site_uid) LEFT JOIN pp USING (site_uid) LEFT JOIN fm.v_site_baseline_status bs USING (site_uid) LEFT JOIN cal USING (site_uid) LEFT JOIN latest USING (site_uid); -- Every view below joins public.v_site for site_uid, site_code, site_name and site_label, so one -- Site filter drives every dataset, and casts to Trino-safe types (varchar(n), integer, -- numeric(p,s), date, timestamp(0), boolean). Live = asserted, reviewed or disputed. -- S2, R3: one row per site and measure; the rows stay when a value is not established. CREATE VIEW public.v_kpi_measures AS WITH cur AS ( -- the site's L/h version; its snapshot only while approved or its inputs unchanged SELECT bs.site_uid, b.baseline_id, b.code, b.fuel_l, b.runtime_h, b.matched_days, b.window_start, b.window_end, (b.status = 'approved' OR coalesce(val.inputs_unchanged, false)) AS shown, ' (' || b.status || ')' || CASE WHEN coalesce(val.inputs_unchanged, false) THEN '' WHEN b.status = 'approved' THEN '; inputs changed since approval: review needed' ELSE '; inputs changed since computation: re-propose' END AS state FROM fm.v_site_baseline_status bs JOIN fm.baseline_version b ON b.code = bs.baseline_code LEFT JOIN fm.v_baseline_validity val ON val.baseline_id = b.baseline_id), ninp AS (SELECT baseline_id, count(*) FILTER (WHERE included) AS n FROM fm.baseline_input GROUP BY baseline_id), m4 AS (SELECT site_uid, sum(consumed_l) AS fuel_l, sum(days) AS days, min(start_on) AS first_on, max(end_on) AS last_on FROM fm.v_m4_interval WHERE eligibility = 'eligible' GROUP BY site_uid), span AS (SELECT site_uid, min(visit_on) AS first_on, max(visit_on) AS last_on FROM fm.v_visit_live GROUP BY site_uid), del AS (SELECT v.site_uid, count(*) AS n FROM fm.v_movement_event e JOIN fm.v_visit_live v ON v.visit_id = e.visit_id WHERE e.kind = 'delivery' GROUP BY v.site_uid), rdg AS (SELECT m.site_uid, count(*) AS n FROM fm.hour_meter_reading h JOIN fm.runtime_meter m ON m.meter_id = h.meter_id WHERE h.claim_status IN ('asserted','reviewed','disputed') GROUP BY m.site_uid), kwh AS (SELECT site_uid, sum(kwh) AS kwh, count(kwh) AS n_kwh, min(bill_month) AS first_m, max(bill_month) AS last_m FROM fm.electricity_bill WHERE claim_status IN ('asserted','reviewed') GROUP BY site_uid), -- values never from disputed lday AS ( -- the L/day reference: the latest approved, else proposed, L/day version SELECT DISTINCT ON (b.site_uid) b.site_uid, b.code, b.status, b.value, b.window_start, b.window_end, coalesce(val.inputs_unchanged, false) AS inputs_unchanged FROM fm.baseline_version b LEFT JOIN fm.v_baseline_validity val ON val.baseline_id = b.baseline_id WHERE b.metric = 'fuel_l_per_calendar_day' AND b.status IN ('approved','proposed') ORDER BY b.site_uid, (b.status = 'approved') DESC, b.approved_on DESC NULLS LAST, b.computed_at DESC) SELECT s.site_uid, s.site_code, s.site_name, s.site_label, x.ord::integer AS measure_order, x.measure::varchar(60) AS measure, round(x.value, 2)::numeric(12,2) AS value, x.unit::varchar(12) AS unit, (CASE WHEN x.value IS NULL THEN 'not established' ELSE x.kind END)::varchar(16) AS value_kind, x.window_start, x.window_end, x.note::varchar(200) AS note FROM public.v_site s LEFT JOIN cur ON cur.site_uid = s.site_uid LEFT JOIN ninp ON ninp.baseline_id = cur.baseline_id LEFT JOIN m4 ON m4.site_uid = s.site_uid LEFT JOIN span ON span.site_uid = s.site_uid LEFT JOIN del ON del.site_uid = s.site_uid LEFT JOIN rdg ON rdg.site_uid = s.site_uid LEFT JOIN kwh ON kwh.site_uid = s.site_uid LEFT JOIN lday ON lday.site_uid = s.site_uid CROSS JOIN LATERAL (VALUES (1, 'Matched fuel', CASE WHEN cur.shown THEN cur.fuel_l END, 'L', 'calculated', cur.window_start, cur.window_end, 'fuel used in the intervals of L/h baseline version ' || cur.code || cur.state), (2, 'Matched runtime', CASE WHEN cur.shown THEN cur.runtime_h END, 'h', 'calculated', cur.window_start, cur.window_end, 'generator running hours in the same intervals' || coalesce(cur.state, '')), (3, 'Runtime per day', CASE WHEN cur.shown THEN cur.runtime_h / NULLIF(cur.matched_days, 0) END, 'h/day', 'calculated', cur.window_start, cur.window_end, 'matched runtime / matched days' || coalesce(cur.state, '')), (4, 'Intervals used', CASE WHEN cur.shown THEN ninp.n::numeric END, 'intervals', 'calculated', cur.window_start, cur.window_end, 'included inputs of the L/h baseline version' || coalesce(cur.state, '')), (5, 'Fuel per calendar day, whole record', m4.fuel_l / NULLIF(m4.days, 0), 'L/day', 'calculated', m4.first_on, m4.last_on, 'stock balance over all usable intervals / calendar days; not per running hour'), (6, 'L/day reference (baseline version)', CASE WHEN lday.status = 'approved' OR lday.inputs_unchanged THEN lday.value END, 'L/day', 'calculated', lday.window_start, lday.window_end, CASE WHEN lday.code IS NULL THEN 'no L/day baseline version' ELSE 'version ' || lday.code || ' (' || lday.status || '), fixed window; not per running hour' || CASE WHEN lday.inputs_unchanged THEN '' WHEN lday.status = 'approved' THEN '; inputs changed since approval: review needed' ELSE '; inputs changed since computation: re-propose' END END), (7, 'Days covered by usable stock intervals', m4.days::numeric, 'days', 'calculated', m4.first_on, m4.last_on, 'compare with the record span'), (8, 'Record span', (span.last_on - span.first_on)::numeric, 'days', 'recorded', span.first_on, span.last_on, 'first to last live record'), (9, 'Deliveries recorded', coalesce(del.n, 0)::numeric, 'deliveries', 'recorded', span.first_on, span.last_on, 'one per delivery event'), (10, 'Timer readings', coalesce(rdg.n, 0)::numeric, 'readings', 'recorded', NULL::date, NULL::date, CASE WHEN coalesce(rdg.n, 0) = 0 THEN 'no hour-meter readings' END), (11, 'Grid energy', kwh.kwh, 'kWh', 'recorded', kwh.first_m, kwh.last_m, CASE WHEN coalesce(kwh.n_kwh, 0) = 0 THEN 'no kWh records; bills are cost only and are not converted to fuel or hours' END) ) AS x(ord, measure, value, unit, kind, window_start, window_end, note); -- R2: timer readings as recorded (no rows in v1). CREATE VIEW public.v_hour_meter_evidence AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, m.source_label::varchar(40) AS meter_label, (CASE WHEN m.asset_uid IS NULL THEN 'unmapped' ELSE m.asset_uid || ' (' || m.mapping_status || ')' END)::varchar(60) AS mapped_set, v.visit_on AS reading_date, h.date_basis::varchar(30) AS date_basis, round(h.hours, 1)::numeric(9,1) AS hours, h.reading_state::varchar(16) AS reading_state, h.source_field::varchar(16) AS source_field, e.epoch_no::integer AS epoch_no, h.claim_status::varchar(12) AS claim_status FROM fm.hour_meter_reading h JOIN fm.runtime_meter m ON m.meter_id = h.meter_id JOIN fm.visit v ON v.visit_id = h.visit_id JOIN public.v_site s ON s.site_uid = m.site_uid LEFT JOIN fm.meter_epoch e ON e.epoch_id = h.epoch_id WHERE h.claim_status IN ('asserted','reviewed','disputed'); -- B5: assumption versions in force or proposed; a coverage assumption lists its members. CREATE VIEW public.v_assumption AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, x.code::varchar(60) AS code, x.topic::varchar(30) AS topic, x.asset_uid::varchar(40) AS asset_uid, x.value_num::numeric(10,3) AS value, x.low_num::numeric(10,3) AS value_low, x.high_num::numeric(10,3) AS value_high, x.unit::varchar(20) AS unit, x.applies_from, x.applies_to, x.basis::varchar(20) AS basis, x.derived_from_fuel, x.status::varchar(12) AS status, x.accepted_on, x.decision_ref::varchar(12) AS decision_ref, x.rationale::varchar(400) AS rationale, x.contradicted_by::varchar(200) AS contradicted_by, mem.members::varchar(400) AS members FROM fm.assumption x JOIN public.v_site s ON s.site_uid = x.site_uid LEFT JOIN LATERAL ( SELECT string_agg(m.member_role || ': ' || coalesce(m.meter_id, m.asset_uid), '; ' ORDER BY m.member_role, coalesce(m.meter_id, m.asset_uid)) AS members FROM fm.assumption_member m WHERE m.assumption_id = x.assumption_id) mem ON true WHERE x.status <> 'superseded'; -- B1: baseline versions, with whether their inputs are unchanged today. CREATE VIEW public.v_baseline_version AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, b.code::varchar(60) AS code, b.metric::varchar(40) AS metric, b.status::varchar(12) AS status, b.population::varchar(200) AS population, round(b.value, 3)::numeric(8,3) AS value, round(b.range_low, 3)::numeric(8,3) AS range_low, round(b.range_high, 3)::numeric(8,3) AS range_high, (CASE WHEN b.metric = 'fuel_l_per_calendar_day' THEN 'L/day' ELSE 'L/h' END)::varchar(8) AS unit, b.window_start, b.window_end, round(b.fuel_l, 1)::numeric(10,1) AS fuel_l, round(b.runtime_h, 1)::numeric(10,1) AS runtime_h, b.matched_days::integer AS matched_days, b.policy_version::varchar(10) AS policy_version, b.meets_policy, b.policy_check::varchar(300) AS policy_check, b.evidence_grade::varchar(2) AS evidence_grade, b.conditions::varchar(200) AS conditions, b.season_note::varchar(200) AS season_note, b.approved_on, b.approved_by_role::varchar(40) AS approved_by_role, b.decision_ref::varchar(12) AS decision_ref, coalesce(val.inputs_unchanged, false) AS inputs_unchanged, (b.computed_at AT TIME ZONE 'Asia/Kabul')::timestamp(0) AS computed_at FROM fm.baseline_version b JOIN public.v_site s ON s.site_uid = b.site_uid LEFT JOIN fm.v_baseline_validity val ON val.baseline_id = b.baseline_id; -- B2: the inputs of each version, with dependency drift since computation. CREATE VIEW public.v_baseline_input AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, b.code::varchar(60) AS baseline_code, b.status::varchar(12) AS baseline_status, (to_char(bi.start_on, 'YYYY-MM-DD') || ' to ' || to_char(bi.end_on, 'YYYY-MM-DD'))::varchar(30) AS interval_label, bi.start_on, bi.end_on, bi.included, bi.exclusion_reason::varchar(300) AS exclusion_reason, round(bi.fuel_l, 1)::numeric(10,1) AS fuel_l, round(bi.runtime_h, 1)::numeric(10,1) AS runtime_h, bi.days::integer AS days, bi.eligibility_at_computation::varchar(12) AS eligibility_at_computation, bi.evidence_grade_at_computation::varchar(2) AS evidence_grade_at_computation, (d.interval_key IS NULL OR d.stock_dep_sha256 <> bi.stock_dep_sha256 OR (b.metric <> 'fuel_l_per_calendar_day' AND d.runtime_dep_sha256 IS DISTINCT FROM bi.runtime_dep_sha256)) AS dependencies_changed FROM fm.baseline_input bi JOIN fm.baseline_version b ON b.baseline_id = bi.baseline_id JOIN public.v_site s ON s.site_uid = bi.site_uid LEFT JOIN fm.v_interval_dependency d ON d.open_visit_id = bi.open_visit_id AND d.close_visit_id = bi.close_visit_id; -- B7: documents the site's figures rest on (the document itself is never served). CREATE VIEW public.v_evidence AS WITH cites AS ( SELECT site_uid, source_sha256, 'fuel movement' AS record_type FROM fm.fuel_movement WHERE claim_status IN ('asserted','reviewed','disputed') UNION SELECT site_uid, source_sha256, 'stock reading' FROM fm.stock_observation WHERE claim_status IN ('asserted','reviewed','disputed') UNION SELECT m.site_uid, h.source_sha256, 'timer reading' FROM fm.hour_meter_reading h JOIN fm.runtime_meter m ON m.meter_id = h.meter_id WHERE h.claim_status IN ('asserted','reviewed','disputed') UNION SELECT site_uid, source_sha256, 'electricity bill' FROM fm.electricity_bill WHERE claim_status IN ('asserted','reviewed','disputed') UNION SELECT site_uid, source_sha256, 'imported calculation' FROM fm.imported_calculation WHERE site_uid IS NOT NULL AND claim_status IN ('asserted','reviewed','disputed') UNION SELECT p.site_uid, e.source_sha256, 'equipment specification' FROM fm.equipment_spec e JOIN fm.asset_placement p ON p.asset_uid = e.asset_uid WHERE e.source_sha256 IS NOT NULL AND e.claim_status IN ('asserted','reviewed','disputed') UNION SELECT site_uid, source_sha256, 'assumption' FROM fm.assumption WHERE source_sha256 IS NOT NULL AND status <> 'superseded') SELECT s.site_uid, s.site_code, s.site_name, s.site_label, left(d.sha256, 16)::varchar(16) AS document_key, d.title::varchar(200) AS title, d.kind::varchar(20) AS kind, d.issuer::varchar(80) AS issuer, d.document_date, string_agg(c.record_type, ', ' ORDER BY c.record_type)::varchar(200) AS used_for, d.contains_personal_data FROM cites c JOIN fm.source_document d ON d.sha256 = c.source_sha256 JOIN public.v_site s ON s.site_uid = c.site_uid GROUP BY s.site_uid, s.site_code, s.site_name, s.site_label, d.sha256, d.title, d.kind, d.issuer, d.document_date, d.contains_personal_data; -- F1, F4: one row per delivery event; quantity NULL = not recorded. No monthly sums. CREATE VIEW public.v_delivery AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, e.event_key::varchar(80) AS event_key, v.visit_on AS delivery_date, v.seq_in_day::integer AS seq_in_day, round(e.litres, 1)::numeric(8,1) AS delivered_l, b.unit_price::numeric(8,2) AS unit_price, b.price_currency::varchar(8) AS currency, b.record_ref::varchar(60) AS source_ref, e.n_claims::integer AS n_sources, (e.spread_l > 0) AS sources_disagree, e.all_reviewed AS reviewed FROM fm.v_movement_event e JOIN fm.v_visit_live v ON v.visit_id = e.visit_id JOIN public.v_site s ON s.site_uid = v.site_uid CROSS JOIN LATERAL ( -- price and reference of the best-ranked claim; -- a disputed claim gives the reference only, never a price SELECT CASE WHEN m.claim_status <> 'disputed' THEN m.unit_price END AS unit_price, CASE WHEN m.claim_status <> 'disputed' THEN m.price_currency END AS price_currency, m.record_ref FROM fm.fuel_movement m JOIN fm.v_source_rank r ON r.sha256 = m.source_sha256 WHERE m.event_key = e.event_key AND m.claim_status IN ('asserted','reviewed','disputed') ORDER BY (m.claim_status = 'disputed'), r.rank, m.movement_id LIMIT 1) b WHERE e.kind = 'delivery'; -- F2: stock readings as recorded, plus after-delivery points derived as before + delivered. CREATE VIEW public.v_stock_reading AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, v.visit_on AS observed_on, v.seq_in_day::integer AS seq_in_day, (CASE st.phase WHEN 'before_delivery' THEN 'before delivery' WHEN 'after_delivery' THEN 'after delivery' ELSE 'stock check' END)::varchar(20) AS reading_type, round(st.litres, 1)::numeric(8,1) AS litres, 'recorded'::varchar(12) AS value_kind, round(st.spread_l, 1)::numeric(8,1) AS spread_l, st.all_reviewed AS reviewed FROM fm.v_visit_stock st JOIN fm.v_visit_live v ON v.visit_id = st.visit_id JOIN public.v_site s ON s.site_uid = v.site_uid UNION ALL SELECT s.site_uid, s.site_code, s.site_name, s.site_label, p.visit_on, p.seq_in_day::integer, 'after delivery'::varchar(20), round(p.depart_l, 1)::numeric(8,1), 'calculated'::varchar(12), round(p.depart_spread_l, 1)::numeric(8,1), p.depart_reviewed FROM fm.v_stock_point p JOIN public.v_site s ON s.site_uid = p.site_uid WHERE p.depart_basis = 'derived'; -- F3, F4, B3, B4: one row per stock interval, with its L/h evaluation. The approved baseline is set -- beside an interval only when both have the same metric and population (B4 compares like with like). CREATE VIEW public.v_interval AS WITH cur AS (SELECT bs.site_uid, bs.baseline_code, bs.baseline_status, bs.lph_estimate, b.metric, b.population_key FROM fm.v_site_baseline_status bs LEFT JOIN fm.baseline_version b ON b.code = bs.baseline_code), inb AS ( -- the inputs of each site's KPI version SELECT bi.open_visit_id, bi.close_visit_id, bi.included FROM fm.baseline_input bi JOIN fm.baseline_version b ON b.baseline_id = bi.baseline_id JOIN cur ON cur.baseline_code = b.code), cond AS ( -- assumptions a valid interval depends on SELECT l.interval_key, string_agg(DISTINCT x.code, ', ') AS codes FROM fm.v_lph_interval l JOIN fm.assumption x ON x.site_uid = l.site_uid AND x.status IN ('proposed','accepted') AND x.topic IN ('generator_idle','meter_coverage') AND daterange(x.applies_from, x.applies_to, '[]') @> daterange(l.start_on, l.end_on, '[]') WHERE l.population_valid GROUP BY l.interval_key) SELECT s.site_uid, s.site_code, s.site_name, s.site_label, (to_char(l.start_on, 'YYYY-MM-DD') || ' to ' || to_char(l.end_on, 'YYYY-MM-DD'))::varchar(30) AS interval_label, l.start_on, l.end_on, l.days::integer AS days, round(l.opening_l, 1)::numeric(9,1) AS opening_l, l.opening_basis::varchar(8) AS opening_basis, round(l.deliveries_between_l, 1)::numeric(9,1) AS deliveries_l, round(l.out_l, 1)::numeric(9,1) AS withdrawals_l, round(l.closing_l, 1)::numeric(9,1) AS closing_l, l.closing_basis::varchar(8) AS closing_basis, round(l.consumed_l, 1)::numeric(9,1) AS fuel_used_l, round(l.consumed_unc_l, 1)::numeric(9,1) AS fuel_used_unc_l, round(l.consumed_l / NULLIF(l.days, 0), 1)::numeric(8,1) AS fuel_l_per_day, l.stock_status::varchar(40) AS stock_status, round(l.runtime_h, 1)::numeric(9,1) AS runtime_h, round(l.runtime_h / NULLIF(l.days, 0), 2)::numeric(6,2) AS runtime_h_per_day, coalesce(l.n_meters, 0)::integer AS n_meters, l.metric::varchar(40) AS metric, l.population_key::varchar(200) AS population_key, -- pool only within one metric and population round(l.l_per_h, 3)::numeric(8,3) AS l_per_h, round(l.l_per_h_low, 3)::numeric(8,3) AS l_per_h_low, round(l.l_per_h_high, 3)::numeric(8,3) AS l_per_h_high, l.eligibility::varchar(12) AS eligibility, nullif(l.exclusion_reasons, '')::varchar(600) AS exclusion_reasons, l.evidence_grade::varchar(2) AS evidence_grade, cond.codes::varchar(200) AS conditions, coalesce(inb.included, false) AS in_current_baseline, coalesce(cur.baseline_status = 'approved' AND l.metric = cur.metric AND l.population_key = cur.population_key, false) AS baseline_comparable, (CASE WHEN cur.baseline_status = 'approved' AND l.metric = cur.metric AND l.population_key = cur.population_key THEN cur.lph_estimate END)::numeric(8,3) AS current_baseline_l_per_h FROM fm.v_lph_interval l JOIN public.v_site s ON s.site_uid = l.site_uid JOIN cur ON cur.site_uid = l.site_uid LEFT JOIN inb ON inb.open_visit_id = l.open_visit_id AND inb.close_visit_id = l.close_visit_id LEFT JOIN cond ON cond.interval_key = l.interval_key; -- R1: hours per day by meter interval (no rows in v1). Every pair is listed with its flags; runtime -- and h/day only for a valid pair (one epoch, not decreasing, at most 24 h per day). CREATE VIEW public.v_runtime AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, m.source_label::varchar(40) AS meter_label, (CASE WHEN m.asset_uid IS NULL THEN 'unmapped' ELSE m.asset_uid || ' (' || m.mapping_status || ')' END)::varchar(60) AS mapped_set, (m.source_label || ': ' || to_char(r.start_on, 'YYYY-MM-DD') || ' to ' || to_char(r.end_on, 'YYYY-MM-DD'))::varchar(80) AS interval_label, r.start_on, r.end_on, r.days::integer AS days, (CASE WHEN v.pair_valid THEN round(r.runtime_h, 1) END)::numeric(9,1) AS runtime_h, (CASE WHEN v.pair_valid THEN round(r.h_per_day, 2) END)::numeric(6,2) AS h_per_day, v.pair_valid, r.same_epoch, r.monotonic, r.over_24h_per_day, r.reviewed FROM fm.v_runtime_pair r CROSS JOIN LATERAL (SELECT coalesce(r.same_epoch AND r.monotonic AND NOT r.over_24h_per_day, false) AS pair_valid) v JOIN fm.runtime_meter m ON m.meter_id = r.meter_id JOIN public.v_site s ON s.site_uid = r.site_uid; -- S3 (Gantt): which periods each kind of evidence covers; Kabul midnight as timestamp(0). CREATE VIEW public.v_coverage AS WITH spans AS ( SELECT site_uid, '1 Stock intervals' AS lane, stock_status AS status, start_on, end_on FROM fm.v_stock_interval UNION ALL SELECT r.site_uid, '2 Runtime: ' || m.source_label, CASE WHEN r.same_epoch AND r.monotonic AND NOT r.over_24h_per_day THEN 'ok' ELSE 'flagged' END, r.start_on, r.end_on FROM fm.v_runtime_pair r JOIN fm.runtime_meter m ON m.meter_id = r.meter_id UNION ALL SELECT site_uid, '3 Baseline window', baseline_status, lph_window_start, lph_window_end FROM fm.v_site_baseline_status WHERE lph_window_start IS NOT NULL UNION ALL SELECT site_uid, '4 Electricity bills', CASE WHEN claim_status = 'disputed' THEN 'disputed' WHEN kwh IS NULL THEN 'cost only' ELSE 'kWh' END, coalesce(period_start, bill_month), coalesce(period_end, (bill_month + interval '1 month')::date) FROM fm.electricity_bill WHERE claim_status IN ('asserted','reviewed','disputed')) SELECT s.site_uid, s.site_code, s.site_name, s.site_label, x.lane::varchar(60) AS lane, x.status::varchar(40) AS status, x.start_on, x.end_on, x.start_on::timestamp(0) AS start_ts, x.end_on::timestamp(0) AS end_ts FROM spans x JOIN public.v_site s ON s.site_uid = x.site_uid; -- B9: stock-balance fuel against engine-measured fuel inside the interval. No rows until flow-meter -- data exist. The window runs from the opening visit's departure to the closing visit's arrival -- (Kabul time) when both times are recorded, else from midnight to midnight. covered_h = the hours -- of that window the generator's records cover (live records never overlap per asset). The -- difference is shown only for an `ok` stock interval with both times recorded, in which every -- generator is placed at the site throughout (one placement spans the interval, as in -- fm.v_lph_interval) and covered for the whole window. CREATE VIEW public.v_reconciliation AS WITH gen AS ( -- one row per interval and generator placed at the site at any time in it SELECT l.interval_key, l.site_uid, l.start_on, l.end_on, l.consumed_l, l.stock_status, p.asset_uid, bool_or(daterange(coalesce(p.occupied_from, '-infinity'::date), p.occupied_to, '[)') @> daterange(l.start_on, l.end_on, '[]')) AS placed_throughout, (o.time_out_local IS NOT NULL AND c.time_in_local IS NOT NULL) AS times_known, (l.start_on + coalesce(o.time_out_local, '00:00'::time)) AT TIME ZONE 'Asia/Kabul' AS t0, (l.end_on + coalesce(c.time_in_local, '00:00'::time)) AT TIME ZONE 'Asia/Kabul' AS t1 FROM fm.v_stock_interval l JOIN fm.visit o ON o.visit_id = l.open_visit_id JOIN fm.visit c ON c.visit_id = l.close_visit_id JOIN fm.asset_placement p ON p.site_uid = l.site_uid JOIN fm.asset a ON a.asset_uid = p.asset_uid AND a.kind = 'generator' WHERE daterange(coalesce(p.occupied_from, '-infinity'::date), p.occupied_to, '[)') && daterange(l.start_on, l.end_on, '[]') GROUP BY l.interval_key, l.site_uid, l.start_on, l.end_on, l.consumed_l, l.stock_status, p.asset_uid, o.time_out_local, c.time_in_local), eng AS ( SELECT g.interval_key, g.asset_uid, sum(t.net_fuel_l) AS engine_l, sum(extract(epoch FROM t.ended_at - t.started_at)) / 3600 AS covered_h, sum(extract(epoch FROM t.ended_at - t.started_at)) >= extract(epoch FROM g.t1 - g.t0) AS full_cover FROM gen g JOIN fm.engine_fuel_total t ON t.asset_uid = g.asset_uid AND t.claim_status IN ('asserted','reviewed') AND t.started_at >= g.t0 AND t.ended_at <= g.t1 GROUP BY g.interval_key, g.asset_uid, g.t0, g.t1), tot AS ( SELECT g.interval_key, count(*) AS n_generators, count(*) FILTER (WHERE g.placed_throughout AND e.full_cover) AS n_complete, sum(e.engine_l) AS engine_all_l FROM gen g LEFT JOIN eng e ON e.interval_key = g.interval_key AND e.asset_uid = g.asset_uid GROUP BY g.interval_key) SELECT s.site_uid, s.site_code, s.site_name, s.site_label, (to_char(g.start_on, 'YYYY-MM-DD') || ' to ' || to_char(g.end_on, 'YYYY-MM-DD'))::varchar(30) AS interval_label, g.start_on, g.end_on, g.stock_status::varchar(40) AS stock_status, g.times_known AS endpoint_times_known, g.asset_uid::varchar(40) AS asset_uid, g.placed_throughout, round(g.consumed_l, 1)::numeric(9,1) AS stock_balance_l, round(e.engine_l, 1)::numeric(9,1) AS engine_l, round(e.covered_h, 1)::numeric(9,1) AS covered_h, round(extract(epoch FROM g.t1 - g.t0) / 3600, 1)::numeric(9,1) AS interval_h, round(t.engine_all_l, 1)::numeric(9,1) AS engine_all_generators_l, (t.n_complete = t.n_generators) AS all_generators_metered, (CASE WHEN t.n_complete = t.n_generators AND g.times_known AND g.stock_status = 'ok' THEN round(g.consumed_l - t.engine_all_l, 1) END)::numeric(9,1) AS difference_l, (CASE WHEN t.n_complete = t.n_generators AND g.times_known AND g.stock_status = 'ok' THEN round(100 * (g.consumed_l - t.engine_all_l) / NULLIF(t.engine_all_l, 0), 1) END)::numeric(7,1) AS difference_pct FROM gen g JOIN eng e ON e.interval_key = g.interval_key AND e.asset_uid = g.asset_uid JOIN tot t ON t.interval_key = g.interval_key JOIN public.v_site s ON s.site_uid = g.site_uid; -- B6: one row per site and check; rows stay when ok. CREATE VIEW public.v_data_quality AS WITH c AS ( SELECT s.site_uid, (SELECT count(*) FROM fm.stock_observation o WHERE o.site_uid = s.site_uid AND o.litres_basis = 'placeholder_zero' AND o.claim_status IN ('asserted','reviewed','disputed')) AS n_placeholder, (SELECT count(*) FROM fm.v_movement_event e JOIN fm.visit v ON v.visit_id = e.visit_id WHERE v.site_uid = s.site_uid AND e.spread_l > 0) + (SELECT count(*) FROM fm.v_visit_stock st JOIN fm.visit v ON v.visit_id = st.visit_id WHERE v.site_uid = s.site_uid AND st.spread_l > 0) AS n_conflicts, (SELECT count(*) FROM fm.hour_meter_reading h JOIN fm.runtime_meter m ON m.meter_id = h.meter_id WHERE m.site_uid = s.site_uid AND h.claim_status IN ('asserted','reviewed','disputed')) AS n_readings, (SELECT count(*) FROM fm.runtime_meter m WHERE m.site_uid = s.site_uid AND m.mapping_status <> 'confirmed') AS n_unmapped, (SELECT count(*) FROM fm.asset_placement p JOIN fm.asset a ON a.asset_uid = p.asset_uid WHERE p.site_uid = s.site_uid AND a.kind = 'generator' AND daterange(coalesce(p.occupied_from, '-infinity'::date), p.occupied_to, '[)') @> (now() AT TIME ZONE 'Asia/Kabul')::date AND NOT EXISTS (SELECT 1 FROM fm.runtime_meter m JOIN fm.hour_meter_reading h ON h.meter_id = m.meter_id WHERE m.asset_uid = p.asset_uid AND m.mapping_status = 'confirmed' AND h.claim_status IN ('asserted','reviewed'))) AS n_gen_no_readings, (SELECT count(*) FROM fm.fuel_movement x WHERE x.site_uid = s.site_uid AND x.claim_status = 'asserted') + (SELECT count(*) FROM fm.stock_observation x WHERE x.site_uid = s.site_uid AND x.claim_status = 'asserted') + (SELECT count(*) FROM fm.hour_meter_reading x JOIN fm.runtime_meter m ON m.meter_id = x.meter_id WHERE m.site_uid = s.site_uid AND x.claim_status = 'asserted') + (SELECT count(*) FROM fm.electricity_bill x WHERE x.site_uid = s.site_uid AND x.claim_status = 'asserted') AS n_unreviewed, (SELECT count(*) FROM fm.v_stock_point p WHERE p.site_uid = s.site_uid AND abs(p.after_vs_before_plus_delivered_l) > 0.5) AS n_after_mismatch, (SELECT count(*) FROM fm.v_stock_interval i WHERE i.site_uid = s.site_uid AND i.stock_status IN ('negative','same_day')) AS n_neg_same_day, (SELECT count(*) FROM fm.v_stock_interval i WHERE i.site_uid = s.site_uid AND i.stock_status NOT IN ('ok','negative','same_day')) AS n_gaps, (SELECT count(*) FROM fm.electricity_bill b WHERE b.site_uid = s.site_uid AND b.claim_status IN ('asserted','reviewed','disputed')) AS n_bills, (SELECT count(*) FROM fm.electricity_bill b WHERE b.site_uid = s.site_uid AND (b.kwh IS NULL OR b.claim_status = 'disputed') AND b.claim_status IN ('asserted','reviewed','disputed')) AS n_bills_no_kwh, -- no usable kWh (SELECT count(*) FROM fm.fuel_movement m WHERE m.site_uid = s.site_uid AND m.kind IN ('withdrawal','drain','spill','loss_reported','internal_transfer') AND m.claim_status IN ('asserted','reviewed','disputed')) AS n_transfer_records, (SELECT count(*) FROM fm.baseline_version b JOIN fm.v_baseline_validity v ON v.baseline_id = b.baseline_id WHERE b.site_uid = s.site_uid AND b.status IN ('proposed','approved') AND NOT v.inputs_unchanged) AS n_drift, (SELECT count(*) FROM fm.v_review_stale r WHERE r.site_uid = s.site_uid) AS n_stale, (SELECT count(*) FROM fm.v_visit_provenance_only v WHERE v.site_uid = s.site_uid) AS n_provenance_only, (SELECT count(*) FROM fm.v_lph_interval l WHERE l.site_uid = s.site_uid AND l.exclusion_reasons LIKE '%confirmed for a generator not placed here%') AS n_unplaced_counter, (SELECT count(*) FROM fm.v_lph_interval l WHERE l.site_uid = s.site_uid AND l.exclusion_reasons LIKE '%meter-coverage assumption % does not name%') AS n_cov_mismatch FROM fm.site s) SELECT vs.site_uid, vs.site_code, vs.site_name, vs.site_label, x.ord::integer AS check_order, x.code::varchar(40) AS check_code, x.label::varchar(80) AS label, x.status::varchar(10) AS status, x.detail::varchar(300) AS detail FROM c JOIN public.v_site vs ON vs.site_uid = c.site_uid CROSS JOIN LATERAL (VALUES (1, 'placeholder_rows', 'Placeholder rows', CASE WHEN c.n_placeholder > 0 THEN 'warning' ELSE 'ok' END, c.n_placeholder || ' row(s) imported as "no stock reading", never as 0'), (2, 'claim_conflicts', 'Conflicting claims', CASE WHEN c.n_conflicts > 0 THEN 'conflict' ELSE 'ok' END, c.n_conflicts || ' movement or stock reading(s) with disagreeing sources'), (3, 'runtime_evidence', 'Runtime evidence', CASE WHEN c.n_readings = 0 THEN 'missing' ELSE 'ok' END, CASE WHEN c.n_readings = 0 THEN 'no hour-meter readings: no L/h can be calculated' ELSE c.n_readings || ' timer reading(s)' END), (4, 'location', 'Location', CASE vs.location_status WHEN 'accepted' THEN 'ok' WHEN 'awaiting acceptance' THEN 'warning' ELSE 'missing' END, vs.location_status::text), (5, 'unmapped_meters', 'Counters not tied to a generator', CASE WHEN c.n_unmapped > 0 THEN 'warning' ELSE 'ok' END, c.n_unmapped || ' counter(s)'), (6, 'generators_without_readings', 'Generators without timer readings', CASE WHEN c.n_gen_no_readings > 0 THEN 'missing' ELSE 'ok' END, c.n_gen_no_readings || ' generator(s) placed now'), (7, 'unreviewed_claims', 'Claims not yet reviewed against the originals', CASE WHEN c.n_unreviewed > 0 THEN 'warning' ELSE 'ok' END, c.n_unreviewed || ' claim(s)'), (8, 'after_vs_before_plus_delivered', 'Stated after-delivery stock differs from before + delivered', CASE WHEN c.n_after_mismatch > 0 THEN 'conflict' ELSE 'ok' END, c.n_after_mismatch || ' visit(s)'), (9, 'negative_or_same_day_intervals', 'Negative or same-day stock intervals', CASE WHEN c.n_neg_same_day > 0 THEN 'warning' ELSE 'ok' END, c.n_neg_same_day || ' interval(s)'), (10, 'interval_gaps', 'Stock intervals excluded for missing or unordered data', CASE WHEN c.n_gaps > 0 THEN 'warning' ELSE 'ok' END, c.n_gaps || ' interval(s)'), (11, 'no_kwh', 'Grid energy (kWh)', CASE WHEN c.n_bills = 0 OR c.n_bills_no_kwh > 0 THEN 'missing' ELSE 'ok' END, CASE WHEN c.n_bills = 0 THEN 'no electricity bills' WHEN c.n_bills_no_kwh > 0 THEN c.n_bills_no_kwh || ' bill(s) without usable kWh (cost only or disputed)' ELSE 'kWh on every bill' END), (12, 'transfer_records', 'Transfer, drain or loss records (HD-05)', CASE WHEN c.n_transfer_records = 0 THEN 'warning' ELSE 'ok' END, CASE WHEN c.n_transfer_records = 0 THEN 'none recorded: every stock balance assumes no unrecorded movements' ELSE c.n_transfer_records || ' record(s)' END), (13, 'baseline_input_drift', 'Baseline inputs changed since computation', CASE WHEN c.n_drift > 0 THEN 'warning' ELSE 'ok' END, c.n_drift || ' proposed or approved version(s)'), (14, 'approval_status', 'Baseline approval', CASE WHEN vs.baseline_status = 'approved' THEN 'ok' ELSE 'warning' END, vs.baseline_status_label || coalesce(' - ' || vs.baseline_validity_note, '')), (15, 'stale_reviews', 'Review entries for values changed since', CASE WHEN c.n_stale > 0 THEN 'warning' ELSE 'ok' END, c.n_stale || ' entr(y/ies)'), (16, 'provenance_only_visits', 'Visits kept for provenance only', CASE WHEN c.n_provenance_only > 0 THEN 'warning' ELSE 'ok' END, c.n_provenance_only || ' visit(s) whose records were all retracted or superseded'), (17, 'counter_on_unplaced_generator', 'Counters confirmed for a generator not placed here', CASE WHEN c.n_unplaced_counter > 0 THEN 'conflict' ELSE 'ok' END, c.n_unplaced_counter || ' interval(s)'), (18, 'coverage_assumption_mismatch', 'Meter-coverage assumption does not match the counters read', CASE WHEN c.n_cov_mismatch > 0 THEN 'conflict' ELSE 'ok' END, c.n_cov_mismatch || ' interval(s)') ) AS x(ord, code, label, status, detail); -- B10: specifications, labelled as such; live rows only. A disputed specification is listed with its -- status and without its value. CREATE VIEW public.v_spec_reference AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, p.slot_label::varchar(20) AS slot, e.asset_uid::varchar(40) AS asset_uid, a.kind::varchar(10) AS asset_kind, concat_ws(' ', a.make, a.model)::varchar(120) AS make_model, e.attribute::varchar(40) AS attribute, e.load_pct::numeric(5,1) AS load_pct, (CASE WHEN e.claim_status <> 'disputed' THEN e.value_num END)::numeric(12,3) AS value, (CASE WHEN e.claim_status <> 'disputed' THEN e.value_text END)::varchar(120) AS value_text, e.unit::varchar(20) AS unit, e.basis::varchar(30) AS basis, e.applicability::varchar(12) AS applicability, e.applicability_note::varchar(300) AS applicability_note, 'specification, not measured'::varchar(30) AS value_kind, e.claim_status::varchar(12) AS claim_status FROM fm.equipment_spec e JOIN fm.asset a ON a.asset_uid = e.asset_uid JOIN (SELECT DISTINCT ON (asset_uid, site_uid) asset_uid, site_uid, slot_label FROM fm.asset_placement ORDER BY asset_uid, site_uid, occupied_from DESC NULLS LAST) p ON p.asset_uid = e.asset_uid JOIN public.v_site s ON s.site_uid = p.site_uid WHERE e.claim_status IN ('asserted','reviewed','disputed'); -- M3 sensitivity table, reference tab only (shown only once Q9 approves a runtime assumption). CREATE VIEW public.v_modelled_lph AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, m.code::varchar(60) AS assumption_code, m.status::varchar(12) AS status, m.basis::varchar(20) AS basis, m.h_per_day::numeric(5,2) AS h_per_day, m.h_per_day_low::numeric(5,2) AS h_per_day_low, m.h_per_day_high::numeric(5,2) AS h_per_day_high, m.window_start, m.window_end, m.matched_days::integer AS matched_days, round(m.l_per_day, 1)::numeric(7,1) AS l_per_day, round(m.modelled_l_per_h, 2)::numeric(6,2) AS modelled_l_per_h, round(m.modelled_l_per_h_low, 2)::numeric(6,2) AS modelled_l_per_h_low, round(m.modelled_l_per_h_high, 2)::numeric(6,2) AS modelled_l_per_h_high, 'modelled: L per calendar day / assumed runtime; not measured'::varchar(70) AS value_kind FROM fm.v_modelled_lph m JOIN public.v_site s ON s.site_uid = m.site_uid; -- R4: electricity cost per bill month; cost context only, never joined into fuel metrics. A -- disputed bill stays listed, without amount or kWh. CREATE VIEW public.v_electricity_cost AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, b.bill_month, b.period_start, b.period_end, (b.period_start IS NOT NULL) AS period_known, (CASE WHEN b.claim_status <> 'disputed' THEN b.amount END)::numeric(12,2) AS amount, b.currency::varchar(8) AS currency, b.currency_basis::varchar(10) AS currency_basis, (CASE WHEN b.claim_status <> 'disputed' THEN b.kwh END)::numeric(10,1) AS kwh, b.record_ref::varchar(60) AS source_ref, b.claim_status::varchar(12) AS claim_status FROM fm.electricity_bill b JOIN public.v_site s ON s.site_uid = b.site_uid WHERE b.claim_status IN ('asserted','reviewed','disputed'); -- B8: operator workbook calculations and our assessment; live rows only. A disputed transcription is -- listed with its status and without its formula or value. CREATE VIEW public.v_imported_calculation AS SELECT s.site_uid, s.site_code, s.site_name, s.site_label, c.cell_ref::varchar(40) AS cell_ref, c.source_label::varchar(80) AS source_label, (CASE WHEN c.claim_status <> 'disputed' THEN c.formula END)::varchar(200) AS formula, (CASE WHEN c.claim_status <> 'disputed' THEN c.cached_value END)::numeric(14,6) AS cached_value, c.unit_as_labelled::varchar(30) AS unit_as_labelled, c.our_reading::varchar(300) AS our_reading, c.baseline_use::varchar(12) AS baseline_use, c.exclusion_reason::varchar(300) AS exclusion_reason, left(c.source_sha256, 16)::varchar(16) AS document_key, c.claim_status::varchar(12) AS claim_status FROM fm.imported_calculation c JOIN public.v_site s ON s.site_uid = c.site_uid WHERE c.claim_status IN ('asserted','reviewed','disputed'); COMMIT;