CREATE TABLE fs_meta(policy text PRIMARY KEY,composition text NOT NULL,source_hash char(64) NOT NULL); CREATE TABLE fs_versions( id bigserial PRIMARY KEY,tenant_id bigint NOT NULL,company_id bigint NOT NULL,unit_id bigint NOT NULL, kind text NOT NULL CHECK(kind IN('mapping','result')),version bigint NOT NULL CHECK(version>0), previous_id bigint UNIQUE REFERENCES fs_versions(id),action text NOT NULL CHECK(action IN('prepare','review')), state text NOT NULL CHECK(state IN('prepared','approved','refused')),prepared_host_id bigint NOT NULL, prepared_native_id bigint NOT NULL REFERENCES users(id),prepared_person text NOT NULL,preparer_identity char(64) NOT NULL, host_actor_id bigint NOT NULL,actor_id bigint NOT NULL REFERENCES users(id),data jsonb NOT NULL, source_basis char(64) NOT NULL,reason text NOT NULL CHECK(length(reason)>=10),created_at timestamptz NOT NULL, UNIQUE(tenant_id,company_id,unit_id,kind,version),CHECK(jsonb_typeof(data)='object'), CHECK((action='prepare' AND state='prepared') OR (action='review' AND state IN('approved','refused'))), CHECK(action<>'review' OR (prepared_host_id<>host_actor_id AND prepared_native_id<>actor_id)) ); CREATE TABLE fs_results( tenant_id bigint NOT NULL,company_id bigint NOT NULL,unit_id bigint NOT NULL,snapshot_key uuid NOT NULL, data jsonb NOT NULL,csv_sha256 char(64) NOT NULL,source_basis char(64) NOT NULL,created_at timestamptz NOT NULL, PRIMARY KEY(tenant_id,company_id,unit_id,snapshot_key) ); CREATE TABLE fs_result_lines( tenant_id bigint NOT NULL,company_id bigint NOT NULL,unit_id bigint NOT NULL,snapshot_key uuid NOT NULL, account_id bigint NOT NULL REFERENCES gl_accounts(id),code text NOT NULL,label text NOT NULL,currency text NOT NULL, category text NOT NULL CHECK(category IN('asset','liability','income','expense','unclassified')), opening_net_minor numeric(40,0) NOT NULL,debit_minor numeric(40,0) NOT NULL,credit_minor numeric(40,0) NOT NULL,closing_net_minor numeric(40,0) NOT NULL, PRIMARY KEY(tenant_id,company_id,unit_id,snapshot_key,account_id), FOREIGN KEY(tenant_id,company_id,unit_id,snapshot_key) REFERENCES fs_results(tenant_id,company_id,unit_id,snapshot_key) DEFERRABLE INITIALLY DEFERRED ); CREATE TABLE fs_commands( id bigserial PRIMARY KEY,tenant_id bigint NOT NULL,company_id bigint NOT NULL,unit_id bigint NOT NULL, host_actor_id bigint NOT NULL,actor_id bigint NOT NULL REFERENCES users(id),command_key uuid NOT NULL, request_hash char(64) NOT NULL,binding_hash char(64) NOT NULL,input jsonb NOT NULL,result jsonb NOT NULL,created_at timestamptz NOT NULL, UNIQUE(tenant_id,company_id,unit_id,host_actor_id,command_key) ); CREATE FUNCTION fs_append_only() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN RAISE EXCEPTION 'SGF_INTERNAL_STATEMENTS_APPEND_ONLY'; END; $$; CREATE TRIGGER fs_meta_immutable BEFORE UPDATE OR DELETE ON fs_meta FOR EACH ROW EXECUTE FUNCTION fs_append_only(); CREATE TRIGGER fs_versions_immutable BEFORE UPDATE OR DELETE ON fs_versions FOR EACH ROW EXECUTE FUNCTION fs_append_only(); CREATE TRIGGER fs_results_immutable BEFORE UPDATE OR DELETE ON fs_results FOR EACH ROW EXECUTE FUNCTION fs_append_only(); CREATE TRIGGER fs_lines_immutable BEFORE UPDATE OR DELETE ON fs_result_lines FOR EACH ROW EXECUTE FUNCTION fs_append_only(); CREATE TRIGGER fs_commands_immutable BEFORE UPDATE OR DELETE ON fs_commands FOR EACH ROW EXECUTE FUNCTION fs_append_only();