fuel-db: customer on every row, place and holder for site, licence tables
Draft migrations rewritten in place (PD-16, PD-18), with 0004 for the licence tables (PD-20) and rule tests in fuel-db/tests. Dashboards' column list regenerated; coverage_detail selects operator_uid. Checks: tests/run.sh 106 of 106; 18 views, 686 rows, 0 differences against a reference from the same corpus. Codex's fixes to its inspection are not re-inspected. Bands: persistence, Superset. Rules: 6, 8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e4806f5799
commit
ab553526f6
11 files changed
+1197
-398
No files matched your search
@@ -0,0 +1,91 @@
|
||||
-- 0004_licence.sql: customer licences (PD-20) and plan prices (PD-19), in schema `licence`.
|
||||
-- DRAFT, not applied. Applied after 0003 by a loader acting as fuel_owner (README in this folder).
|
||||
-- fuel_ro has no access: its default privileges cover `public` only. The server gets its own role
|
||||
-- when it starts checking licences. No customer's licence is seeded here; our admins write those.
|
||||
BEGIN;
|
||||
CREATE SCHEMA licence AUTHORIZATION fuel_owner;
|
||||
SET LOCAL search_path = licence, fm, pg_catalog, public; -- fm holds btree_gist
|
||||
|
||||
CREATE TABLE licence.plan (
|
||||
plan_code text PRIMARY KEY,
|
||||
name text NOT NULL
|
||||
);
|
||||
CREATE TABLE licence.plan_price ( -- per sensor per month (PD-19); a change is a new dated row
|
||||
plan_code text NOT NULL REFERENCES licence.plan,
|
||||
payment_term text NOT NULL CHECK (payment_term IN ('annual','monthly')),
|
||||
currency text NOT NULL CHECK (currency ~ '^[A-Z]{3}$'),
|
||||
price numeric(8,2) NOT NULL CHECK (price >= 0),
|
||||
valid_from date NOT NULL, valid_to date, -- NULL to = still valid
|
||||
EXCLUDE USING gist (plan_code WITH =, payment_term WITH =, currency WITH =,
|
||||
daterange(valid_from, valid_to, '[)') WITH &&)
|
||||
);
|
||||
CREATE TABLE licence.plan_entitlement (
|
||||
plan_code text NOT NULL REFERENCES licence.plan,
|
||||
feature text NOT NULL,
|
||||
limit_value integer, -- NULL: no limit
|
||||
PRIMARY KEY (plan_code, feature)
|
||||
);
|
||||
CREATE TABLE licence.licence ( -- a renewal is a new row
|
||||
licence_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
operator_uid text NOT NULL REFERENCES fm.operator,
|
||||
plan_code text NOT NULL REFERENCES licence.plan,
|
||||
payment_term text NOT NULL CHECK (payment_term IN ('annual','monthly')),
|
||||
valid_from date NOT NULL,
|
||||
valid_until date NOT NULL, -- last valid day
|
||||
grace_until date NOT NULL, -- last day of grace; alarms stop after it
|
||||
status text NOT NULL CHECK (status IN ('active','suspended')),
|
||||
contract_ref text NOT NULL, -- the signed agreement or amendment the dates come from
|
||||
created_by text NOT NULL, -- a role, never a person's name
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
CHECK (valid_until >= valid_from),
|
||||
CHECK (grace_until >= valid_until),
|
||||
EXCLUDE USING gist (operator_uid WITH =, daterange(valid_from, valid_until, '[]') WITH &&)
|
||||
);
|
||||
CREATE TABLE licence.licence_override (
|
||||
licence_id bigint NOT NULL REFERENCES licence.licence,
|
||||
feature text NOT NULL,
|
||||
limit_value integer, -- NULL: no limit
|
||||
PRIMARY KEY (licence_id, feature)
|
||||
);
|
||||
|
||||
-- Prices decided on 30 Oct 2026 (PD-19). The pilot plan costs nothing on either term.
|
||||
INSERT INTO licence.plan (plan_code, name) VALUES ('pilot', 'Pilot'), ('standard', 'Standard');
|
||||
INSERT INTO licence.plan_price (plan_code, payment_term, currency, price, valid_from) VALUES
|
||||
('pilot', 'annual', 'USD', 0.00, '2026-01-01'),
|
||||
('pilot', 'monthly', 'USD', 0.00, '2026-01-01'),
|
||||
('standard', 'annual', 'USD', 15.00, '2026-10-30'),
|
||||
('standard', 'monthly', 'USD', 17.00, '2026-10-30');
|
||||
|
||||
-- Every change is logged (PD-20), and the log is never changed.
|
||||
CREATE TABLE licence.licence_event (
|
||||
event_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
table_name text NOT NULL,
|
||||
action text NOT NULL CHECK (action IN ('INSERT','UPDATE','DELETE')),
|
||||
old_row jsonb, new_row jsonb,
|
||||
changed_by text NOT NULL DEFAULT current_user,
|
||||
changed_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE FUNCTION licence.tg_log_change() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
INSERT INTO licence.licence_event (table_name, action, old_row, new_row)
|
||||
VALUES (TG_TABLE_NAME, TG_OP,
|
||||
CASE WHEN TG_OP <> 'INSERT' THEN to_jsonb(OLD) END,
|
||||
CASE WHEN TG_OP <> 'DELETE' THEN to_jsonb(NEW) END);
|
||||
RETURN NULL;
|
||||
END $$;
|
||||
DO $$
|
||||
DECLARE t text;
|
||||
BEGIN
|
||||
FOREACH t IN ARRAY ARRAY['plan','plan_price','plan_entitlement','licence','licence_override'] LOOP
|
||||
EXECUTE format('CREATE TRIGGER log_change AFTER INSERT OR UPDATE OR DELETE ON licence.%I
|
||||
FOR EACH ROW EXECUTE FUNCTION licence.tg_log_change()', t);
|
||||
END LOOP;
|
||||
END $$;
|
||||
CREATE FUNCTION licence.tg_event_immutable() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION 'licence_event: the log is never changed';
|
||||
END $$;
|
||||
CREATE TRIGGER event_immutable BEFORE UPDATE OR DELETE ON licence.licence_event
|
||||
FOR EACH ROW EXECUTE FUNCTION licence.tg_event_immutable();
|
||||
|
||||
COMMIT;
|
||||
Reference in new issue
Block a user