Merge main: Guidad Felsökning flyttar ur roten till felsokning/
Main är sedan den här grenen skapades en helt annan produkt — Semantika,
en mobilapp med egen CDK-infrastruktur. Den äger nu repots rot: en
npm-workspaces-monorepo med apps/mobile, services/api och infra.
För att båda ska rymmas i samma repo flyttar Guidad Felsökning in i en
egen katalog i stället för att göra anspråk på roten:
felsokning/app webbklienten (Vite, egen package.json och
eslint-/vitest-konfiguration)
felsokning/services plattformstjänsten och AI-orkestern
felsokning/infra Terraform och databasschemat
felsokning/docs vision, moduler, drift
felsokning/supabase edge-funktion och migrationer
Merge:n hade tagit bort 128 filer som Guidad Felsökning bygger på —
värdapplikationens komponenter, Supabase-klienten, tillgångar — eftersom
main raderat dem och den här grenen inte råkat ändra just dem. De är
återställda på sin nya plats. Utan dem gick varken bygget eller
testerna: ai.ts och synk.ts importerar Supabase-klienten.
Semantikas rotfiler är orörda: package.json, eslint.config.js och
.github/workflows/ är deras. Guidad Felsökning har egna motsvarigheter i
sin katalog.
CI flyttar samtidigt från GitHub Actions till .gitea/workflows — samma
syntax, egna runners. .github/workflows/ tillhör Semantika härefter.
Verifierat på den nya platsen: 96 vitest-tester, typkontroll, eslint på
både klient och tjänster, bygge, och integrationstest mot riktig Postgres.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
+62
@@ -0,0 +1,62 @@
|
||||
-- Create profiles table for user data
|
||||
CREATE TABLE public.profiles (
|
||||
id UUID NOT NULL DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
user_id UUID NOT NULL UNIQUE REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
display_name TEXT,
|
||||
phone TEXT,
|
||||
address TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- Enable Row Level Security
|
||||
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Users can view their own profile
|
||||
CREATE POLICY "Users can view their own profile"
|
||||
ON public.profiles
|
||||
FOR SELECT
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
-- Users can insert their own profile
|
||||
CREATE POLICY "Users can insert their own profile"
|
||||
ON public.profiles
|
||||
FOR INSERT
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
-- Users can update their own profile
|
||||
CREATE POLICY "Users can update their own profile"
|
||||
ON public.profiles
|
||||
FOR UPDATE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
-- Create function to update timestamps
|
||||
CREATE OR REPLACE FUNCTION public.update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SET search_path = public;
|
||||
|
||||
-- Create trigger for automatic timestamp updates
|
||||
CREATE TRIGGER update_profiles_updated_at
|
||||
BEFORE UPDATE ON public.profiles
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.update_updated_at_column();
|
||||
|
||||
-- Create function to automatically create profile on signup
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO public.profiles (user_id)
|
||||
VALUES (NEW.id);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = public;
|
||||
|
||||
-- Create trigger to auto-create profile on user signup
|
||||
CREATE TRIGGER on_auth_user_created
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.handle_new_user();
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Add company_name column to profiles table
|
||||
ALTER TABLE public.profiles
|
||||
ADD COLUMN company_name text;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Add separate address fields to profiles table
|
||||
ALTER TABLE public.profiles
|
||||
ADD COLUMN street_address text,
|
||||
ADD COLUMN postal_code text,
|
||||
ADD COLUMN city text;
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
-- Create orders table to store e-commerce orders
|
||||
CREATE TABLE public.orders (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
order_number TEXT NOT NULL,
|
||||
customer_email TEXT NOT NULL,
|
||||
customer_name TEXT,
|
||||
items JSONB NOT NULL DEFAULT '[]',
|
||||
total_amount DECIMAL(10,2) NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'SEK',
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
shopify_checkout_id TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE public.orders ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Create policy for authenticated users (admins) to view all orders
|
||||
CREATE POLICY "Authenticated users can view all orders"
|
||||
ON public.orders
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (true);
|
||||
|
||||
-- Create policy for inserting orders (from edge function)
|
||||
CREATE POLICY "Service role can insert orders"
|
||||
ON public.orders
|
||||
FOR INSERT
|
||||
WITH CHECK (true);
|
||||
|
||||
-- Create trigger for updated_at
|
||||
CREATE TRIGGER update_orders_updated_at
|
||||
BEFORE UPDATE ON public.orders
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.update_updated_at_column();
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
-- Create customers table
|
||||
CREATE TABLE public.customers (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
name TEXT,
|
||||
phone TEXT,
|
||||
company TEXT,
|
||||
total_orders INTEGER DEFAULT 0,
|
||||
total_spent DECIMAL(10,2) DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- Create payments table
|
||||
CREATE TABLE public.payments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
order_id UUID REFERENCES public.orders(id) ON DELETE CASCADE,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
currency TEXT NOT NULL DEFAULT 'SEK',
|
||||
status TEXT NOT NULL DEFAULT 'pending',
|
||||
payment_method TEXT,
|
||||
shopify_payment_id TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
-- Add customer reference to orders
|
||||
ALTER TABLE public.orders ADD COLUMN customer_id UUID REFERENCES public.customers(id);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE public.customers ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.payments ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- RLS policies for customers (only authenticated admins)
|
||||
CREATE POLICY "Authenticated users can view customers"
|
||||
ON public.customers FOR SELECT TO authenticated USING (true);
|
||||
|
||||
CREATE POLICY "Authenticated users can manage customers"
|
||||
ON public.customers FOR ALL TO authenticated USING (true) WITH CHECK (true);
|
||||
|
||||
-- RLS policies for payments
|
||||
CREATE POLICY "Authenticated users can view payments"
|
||||
ON public.payments FOR SELECT TO authenticated USING (true);
|
||||
|
||||
CREATE POLICY "Service can insert payments"
|
||||
ON public.payments FOR INSERT WITH CHECK (true);
|
||||
|
||||
-- Triggers for updated_at
|
||||
CREATE TRIGGER update_customers_updated_at
|
||||
BEFORE UPDATE ON public.customers
|
||||
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
|
||||
-- Role enum
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE public.app_role AS ENUM ('admin', 'user');
|
||||
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||
|
||||
-- user_roles table
|
||||
CREATE TABLE IF NOT EXISTS public.user_roles (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
role public.app_role NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (user_id, role)
|
||||
);
|
||||
|
||||
GRANT SELECT ON public.user_roles TO authenticated;
|
||||
GRANT ALL ON public.user_roles TO service_role;
|
||||
|
||||
ALTER TABLE public.user_roles ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
DROP POLICY IF EXISTS "Users can view own roles" ON public.user_roles;
|
||||
CREATE POLICY "Users can view own roles"
|
||||
ON public.user_roles FOR SELECT
|
||||
TO authenticated
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
-- Security definer role check
|
||||
CREATE OR REPLACE FUNCTION public.has_role(_user_id uuid, _role public.app_role)
|
||||
RETURNS boolean
|
||||
LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public
|
||||
AS $$
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.user_roles
|
||||
WHERE user_id = _user_id AND role = _role
|
||||
)
|
||||
$$;
|
||||
|
||||
-- Backfill admin for existing owner
|
||||
INSERT INTO public.user_roles (user_id, role)
|
||||
SELECT id, 'admin'::public.app_role
|
||||
FROM auth.users
|
||||
WHERE lower(email) = 'tiffanysvson@gmail.com'
|
||||
ON CONFLICT (user_id, role) DO NOTHING;
|
||||
|
||||
-- Auto-grant admin on future signup with owner email
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql SECURITY DEFINER SET search_path = public
|
||||
AS $$
|
||||
BEGIN
|
||||
INSERT INTO public.profiles (user_id) VALUES (NEW.id)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
IF lower(NEW.email) = 'tiffanysvson@gmail.com' THEN
|
||||
INSERT INTO public.user_roles (user_id, role)
|
||||
VALUES (NEW.id, 'admin')
|
||||
ON CONFLICT (user_id, role) DO NOTHING;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
REVOKE ALL ON FUNCTION public.has_role(uuid, public.app_role) FROM PUBLIC, anon;
|
||||
GRANT EXECUTE ON FUNCTION public.has_role(uuid, public.app_role) TO authenticated, service_role;
|
||||
|
||||
REVOKE ALL ON FUNCTION public.handle_new_user() FROM PUBLIC, anon, authenticated;
|
||||
|
||||
REVOKE ALL ON FUNCTION public.update_updated_at_column() FROM PUBLIC, anon, authenticated;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
|
||||
-- Orders: admin-only reads
|
||||
DROP POLICY IF EXISTS "Authenticated users can view all orders" ON public.orders;
|
||||
CREATE POLICY "Admins can view orders"
|
||||
ON public.orders FOR SELECT
|
||||
TO authenticated
|
||||
USING (public.has_role(auth.uid(), 'admin'));
|
||||
|
||||
-- Customers: admin-only
|
||||
DROP POLICY IF EXISTS "Authenticated users can manage customers" ON public.customers;
|
||||
DROP POLICY IF EXISTS "Authenticated users can view customers" ON public.customers;
|
||||
CREATE POLICY "Admins can view customers"
|
||||
ON public.customers FOR SELECT
|
||||
TO authenticated
|
||||
USING (public.has_role(auth.uid(), 'admin'));
|
||||
CREATE POLICY "Admins can manage customers"
|
||||
ON public.customers FOR ALL
|
||||
TO authenticated
|
||||
USING (public.has_role(auth.uid(), 'admin'))
|
||||
WITH CHECK (public.has_role(auth.uid(), 'admin'));
|
||||
|
||||
-- Payments: admin-only reads
|
||||
DROP POLICY IF EXISTS "Authenticated users can view payments" ON public.payments;
|
||||
CREATE POLICY "Admins can view payments"
|
||||
ON public.payments FOR SELECT
|
||||
TO authenticated
|
||||
USING (public.has_role(auth.uid(), 'admin'));
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
DROP POLICY IF EXISTS "Service role can insert orders" ON public.orders;
|
||||
DROP POLICY IF EXISTS "Service can insert payments" ON public.payments;
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
-- Restrict direct execution of SECURITY DEFINER function has_role.
|
||||
-- Switching to SECURITY INVOKER: when called as auth.uid() the user_roles
|
||||
-- RLS policy already lets a user see (only) their own role rows, so
|
||||
-- has_role(auth.uid(), 'admin') still returns correctly.
|
||||
-- We also revoke EXECUTE from anon/public so the function cannot be
|
||||
-- invoked directly from the Data API.
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.has_role(_user_id uuid, _role app_role)
|
||||
RETURNS boolean
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
SECURITY INVOKER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM public.user_roles
|
||||
WHERE user_id = _user_id
|
||||
AND role = _role
|
||||
)
|
||||
$$;
|
||||
|
||||
REVOKE EXECUTE ON FUNCTION public.has_role(uuid, app_role) FROM PUBLIC;
|
||||
REVOKE EXECUTE ON FUNCTION public.has_role(uuid, app_role) FROM anon;
|
||||
-- authenticated retains EXECUTE so RLS policies referencing has_role
|
||||
-- continue to work; the function itself is now SECURITY INVOKER so it
|
||||
-- cannot be used to bypass RLS.
|
||||
GRANT EXECUTE ON FUNCTION public.has_role(uuid, app_role) TO authenticated;
|
||||
GRANT EXECUTE ON FUNCTION public.has_role(uuid, app_role) TO service_role;
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
|
||||
CREATE TABLE public.subscriptions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
|
||||
customer_email TEXT NOT NULL,
|
||||
company_name TEXT,
|
||||
stripe_subscription_id TEXT NOT NULL UNIQUE,
|
||||
stripe_customer_id TEXT NOT NULL,
|
||||
price_id TEXT,
|
||||
kg_per_week NUMERIC,
|
||||
monthly_amount NUMERIC,
|
||||
status TEXT NOT NULL DEFAULT 'active',
|
||||
collection_method TEXT,
|
||||
current_period_start TIMESTAMPTZ,
|
||||
current_period_end TIMESTAMPTZ,
|
||||
cancel_at_period_end BOOLEAN NOT NULL DEFAULT false,
|
||||
environment TEXT NOT NULL DEFAULT 'sandbox',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_subscriptions_user_id ON public.subscriptions(user_id);
|
||||
CREATE INDEX idx_subscriptions_customer_email ON public.subscriptions(customer_email);
|
||||
CREATE INDEX idx_subscriptions_stripe_id ON public.subscriptions(stripe_subscription_id);
|
||||
|
||||
GRANT SELECT ON public.subscriptions TO authenticated;
|
||||
GRANT ALL ON public.subscriptions TO service_role;
|
||||
|
||||
ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Users can view own subscriptions"
|
||||
ON public.subscriptions FOR SELECT
|
||||
TO authenticated
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Admins can view all subscriptions"
|
||||
ON public.subscriptions FOR SELECT
|
||||
TO authenticated
|
||||
USING (public.has_role(auth.uid(), 'admin'::app_role));
|
||||
|
||||
CREATE TRIGGER update_subscriptions_updated_at
|
||||
BEFORE UPDATE ON public.subscriptions
|
||||
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.has_active_kaka_subscription(user_uuid UUID)
|
||||
RETURNS BOOLEAN
|
||||
LANGUAGE SQL
|
||||
STABLE
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM public.subscriptions
|
||||
WHERE user_id = user_uuid
|
||||
AND (
|
||||
(status IN ('active','trialing','past_due') AND (current_period_end IS NULL OR current_period_end > now()))
|
||||
OR (status = 'canceled' AND current_period_end > now())
|
||||
)
|
||||
);
|
||||
$$;
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
REVOKE EXECUTE ON FUNCTION public.has_active_kaka_subscription(UUID) FROM PUBLIC, anon, authenticated;
|
||||
GRANT EXECUTE ON FUNCTION public.has_active_kaka_subscription(UUID) TO service_role;
|
||||
@@ -0,0 +1,72 @@
|
||||
-- Guidad Felsökning: ärenden och append-only händelselogg.
|
||||
-- Loggen är enda sanningskällan; alla vyer är projektioner.
|
||||
-- Append-only garanteras även i databasen: inga update/delete-policyer.
|
||||
|
||||
create table public.felsokning_arenden (
|
||||
id text primary key,
|
||||
nummer integer not null,
|
||||
skapad timestamptz not null,
|
||||
skapad_av uuid not null default auth.uid(),
|
||||
delningskod text unique,
|
||||
insatt timestamptz not null default now()
|
||||
);
|
||||
|
||||
create table public.felsokning_handelser (
|
||||
id text primary key,
|
||||
arende_id text not null references public.felsokning_arenden(id),
|
||||
tidpunkt timestamptz not null,
|
||||
anvandare text not null,
|
||||
handelse jsonb not null,
|
||||
insatt timestamptz not null default now()
|
||||
);
|
||||
|
||||
create index felsokning_handelser_arende_idx
|
||||
on public.felsokning_handelser (arende_id, tidpunkt);
|
||||
|
||||
alter table public.felsokning_arenden enable row level security;
|
||||
alter table public.felsokning_handelser enable row level security;
|
||||
|
||||
-- Beta: alla inloggade användare delar arbetsyta (samarbete i ärenden).
|
||||
-- Multi-tenant-uppdelning per organisation införs i nästa fas.
|
||||
create policy "Inloggade läser ärenden"
|
||||
on public.felsokning_arenden for select to authenticated using (true);
|
||||
create policy "Inloggade skapar ärenden"
|
||||
on public.felsokning_arenden for insert to authenticated with check (true);
|
||||
create policy "Inloggade läser händelser"
|
||||
on public.felsokning_handelser for select to authenticated using (true);
|
||||
create policy "Inloggade lägger till händelser"
|
||||
on public.felsokning_handelser for insert to authenticated with check (true);
|
||||
|
||||
-- Inga update/delete-policyer, och rättigheterna återkallas dessutom explicit:
|
||||
-- historiken kan aldrig ändras eller raderas från klienten.
|
||||
revoke update, delete on public.felsokning_arenden from authenticated, anon;
|
||||
revoke update, delete on public.felsokning_handelser from authenticated, anon;
|
||||
|
||||
-- Live Share: anonym läsning sker ENDAST via delningskod genom en
|
||||
-- security definer-funktion — tabellerna exponeras aldrig direkt för anon.
|
||||
-- Interna poster (kategoribyten, hypoteser) filtreras bort ur den delade
|
||||
-- vyn: hypoteser är arbetsmaterial och får aldrig läsas som konstaterade fel.
|
||||
create or replace function public.hamta_delat_arende(kod text)
|
||||
returns jsonb
|
||||
language sql
|
||||
security definer
|
||||
set search_path = public
|
||||
stable
|
||||
as $$
|
||||
select jsonb_build_object(
|
||||
'arende', to_jsonb(a) - 'skapad_av',
|
||||
'handelser', coalesce((
|
||||
select jsonb_agg(to_jsonb(h) order by h.tidpunkt, h.id)
|
||||
from felsokning_handelser h
|
||||
where h.arende_id = a.id
|
||||
and h.handelse->>'typ' not in ('kategori_byte', 'hypotes', 'ai_svar', 'ansvarig_satt', 'arbetsorder_skannad')
|
||||
), '[]'::jsonb)
|
||||
)
|
||||
from felsokning_arenden a
|
||||
where a.delningskod = kod and a.delningskod is not null;
|
||||
$$;
|
||||
|
||||
grant execute on function public.hamta_delat_arende(text) to anon, authenticated;
|
||||
|
||||
-- Realtime-uppdateringar för framtida prenumerationer i klienten.
|
||||
alter publication supabase_realtime add table public.felsokning_handelser;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- Delningsgränsen görs om från nekalista till tillåtelselista.
|
||||
--
|
||||
-- Tidigare filtrerade hamta_delat_arende bort en uppräknad mängd interna
|
||||
-- händelsetyper. Varje ny händelsetyp blev därmed automatiskt synlig för
|
||||
-- kunden tills någon kom ihåg att neka den — fel håll att fela åt på en
|
||||
-- integritetsgräns. Nu räknas i stället upp vad som FÅR delas; allt annat
|
||||
-- är internt tills det aktivt släpps fram.
|
||||
--
|
||||
-- Listan speglar DELBART_KUND i services/plattform/server.mjs, som låses
|
||||
-- av testet src/felsokning/__tests__/delning.test.ts.
|
||||
|
||||
create or replace function public.hamta_delat_arende(kod text)
|
||||
returns jsonb
|
||||
language sql
|
||||
security definer
|
||||
set search_path = public
|
||||
stable
|
||||
as $$
|
||||
select jsonb_build_object(
|
||||
'arende', to_jsonb(a) - 'skapad_av',
|
||||
'handelser', coalesce((
|
||||
select jsonb_agg(to_jsonb(h) order by h.tidpunkt, h.id)
|
||||
from felsokning_handelser h
|
||||
where h.arende_id = a.id
|
||||
and h.handelse->>'typ' in (
|
||||
'objekt_identifierat', 'arendetyp_satt', 'felbeskrivning', 'fraga_besvarad',
|
||||
'kontroll_utford', 'observation', 'matvarde', 'foto', 'video', 'kommentar',
|
||||
'inaktivitet_forklarad', 'overlamning', 'historik_kontrollerad', 'matarstallning',
|
||||
'reproducering', 'felorsak', 'atgardsforslag', 'kundbeslut', 'atgard_utford',
|
||||
'kvalitetskontroll', 'export_skapad', 'arende_avslutat'
|
||||
)
|
||||
), '[]'::jsonb)
|
||||
)
|
||||
from felsokning_arenden a
|
||||
where a.delningskod = kod and a.delningskod is not null;
|
||||
$$;
|
||||
|
||||
grant execute on function public.hamta_delat_arende(text) to anon, authenticated;
|
||||
Reference in New Issue
Block a user