-
+
Anslut ditt bankkonto
-
+
{hasBanking
? 'Koppla via PSD2 — transaktioner synkas automatiskt varje dag.'
: 'Importera kontoutdrag från din bank — CSV, OFX och de flesta svenska banker.'}
diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx
index 75045d53..9c563d89 100644
--- a/components/ui/dialog.tsx
+++ b/components/ui/dialog.tsx
@@ -37,7 +37,7 @@ const DialogContent = React.forwardRef<
p_actual_last
+ AND last_number <= p_reserved_highest;
+END;
+$function$;
+
+-- create_invoice_with_items
+CREATE OR REPLACE FUNCTION public.create_invoice_with_items(p_invoice jsonb, p_items jsonb)
+ RETURNS jsonb
+ LANGUAGE plpgsql
+ SECURITY DEFINER
+ SET search_path TO 'public'
+AS $function$
+DECLARE
+ v_invoice_id uuid;
+ v_invoice_number integer;
+ v_result jsonb;
+BEGIN
+ SELECT COALESCE(MAX(invoice_number::integer), 0) + 1
+ INTO v_invoice_number
+ FROM invoices
+ WHERE user_id = (p_invoice->>'user_id')::uuid;
+
+ INSERT INTO invoices (
+ user_id, customer_id, invoice_number, invoice_date, due_date,
+ status, currency, exchange_rate, exchange_rate_date,
+ subtotal, vat_amount, total,
+ subtotal_sek, vat_amount_sek, total_sek,
+ vat_treatment, vat_rate, moms_ruta,
+ your_reference, our_reference, notes,
+ reverse_charge_text
+ ) VALUES (
+ (p_invoice->>'user_id')::uuid,
+ (p_invoice->>'customer_id')::uuid,
+ v_invoice_number::text,
+ (p_invoice->>'invoice_date')::date,
+ (p_invoice->>'due_date')::date,
+ COALESCE(p_invoice->>'status', 'draft'),
+ COALESCE(p_invoice->>'currency', 'SEK'),
+ (p_invoice->>'exchange_rate')::numeric,
+ (p_invoice->>'exchange_rate_date')::date,
+ (p_invoice->>'subtotal')::numeric,
+ (p_invoice->>'vat_amount')::numeric,
+ (p_invoice->>'total')::numeric,
+ (p_invoice->>'subtotal_sek')::numeric,
+ (p_invoice->>'vat_amount_sek')::numeric,
+ (p_invoice->>'total_sek')::numeric,
+ p_invoice->>'vat_treatment',
+ (p_invoice->>'vat_rate')::numeric,
+ p_invoice->>'moms_ruta',
+ p_invoice->>'your_reference',
+ p_invoice->>'our_reference',
+ p_invoice->>'notes',
+ p_invoice->>'reverse_charge_text'
+ ) RETURNING id INTO v_invoice_id;
+
+ INSERT INTO invoice_items (invoice_id, sort_order, description, quantity, unit, unit_price, line_total)
+ SELECT
+ v_invoice_id,
+ (item->>'sort_order')::integer,
+ item->>'description',
+ (item->>'quantity')::numeric,
+ item->>'unit',
+ (item->>'unit_price')::numeric,
+ (item->>'line_total')::numeric
+ FROM jsonb_array_elements(p_items) AS item;
+
+ SELECT jsonb_build_object(
+ 'id', i.id,
+ 'invoice_number', i.invoice_number,
+ 'invoice_date', i.invoice_date,
+ 'due_date', i.due_date,
+ 'status', i.status,
+ 'currency', i.currency,
+ 'exchange_rate', i.exchange_rate,
+ 'subtotal', i.subtotal,
+ 'vat_amount', i.vat_amount,
+ 'total', i.total,
+ 'subtotal_sek', i.subtotal_sek,
+ 'vat_amount_sek', i.vat_amount_sek,
+ 'total_sek', i.total_sek,
+ 'vat_treatment', i.vat_treatment,
+ 'vat_rate', i.vat_rate,
+ 'moms_ruta', i.moms_ruta,
+ 'your_reference', i.your_reference,
+ 'our_reference', i.our_reference,
+ 'notes', i.notes,
+ 'reverse_charge_text', i.reverse_charge_text,
+ 'customer', jsonb_build_object('id', c.id, 'name', c.name),
+ 'items', (
+ SELECT jsonb_agg(jsonb_build_object(
+ 'id', ii.id,
+ 'sort_order', ii.sort_order,
+ 'description', ii.description,
+ 'quantity', ii.quantity,
+ 'unit', ii.unit,
+ 'unit_price', ii.unit_price,
+ 'line_total', ii.line_total
+ ) ORDER BY ii.sort_order)
+ FROM invoice_items ii WHERE ii.invoice_id = v_invoice_id
+ )
+ )
+ INTO v_result
+ FROM invoices i
+ LEFT JOIN customers c ON c.id = i.customer_id
+ WHERE i.id = v_invoice_id;
+
+ RETURN v_result;
+END;
+$function$;
+
+-- seed_asset_categories
+CREATE OR REPLACE FUNCTION public.seed_asset_categories(p_user_id uuid)
+ RETURNS void
+ LANGUAGE plpgsql
+ SECURITY DEFINER
+ SET search_path TO 'public'
+AS $function$
+begin
+ if exists (select 1 from public.asset_categories where user_id = p_user_id) then
+ return;
+ end if;
+
+ insert into public.asset_categories (user_id, code, name, asset_account, depreciation_account, expense_account, default_useful_life_months, default_depreciation_method, is_system)
+ values
+ (p_user_id, 'BYGGNADER', 'Byggnader', '1110', '1119', '7820', 600, 'straight_line', true),
+ (p_user_id, 'MASKINER', 'Maskiner och tekniska anläggningar', '1210', '1219', '7831', 60, 'straight_line', true),
+ (p_user_id, 'INVENTARIER', 'Inventarier', '1220', '1229', '7832', 60, 'straight_line', true),
+ (p_user_id, 'FORDON', 'Fordon', '1240', '1249', '7834', 60, 'straight_line', true),
+ (p_user_id, 'DATORER', 'Datorer och IT-utrustning','1250', '1259', '7833', 36, 'straight_line', true),
+ (p_user_id, 'IMMATERIELLA', 'Immateriella tillgångar', '1010', '1019', '7810', 60, 'straight_line', true);
+end;
+$function$;
+
+-- update_reconciliation_session_counts (trigger function)
+CREATE OR REPLACE FUNCTION public.update_reconciliation_session_counts()
+ RETURNS trigger
+ LANGUAGE plpgsql
+ SECURITY DEFINER
+ SET search_path TO 'public'
+AS $function$
+begin
+ update public.bank_reconciliation_sessions
+ set
+ matched_count = (
+ select count(*) from public.bank_reconciliation_items
+ where session_id = coalesce(new.session_id, old.session_id)
+ and is_reconciled = true
+ ),
+ unmatched_count = (
+ select count(*) from public.bank_reconciliation_items
+ where session_id = coalesce(new.session_id, old.session_id)
+ and is_reconciled = false
+ ),
+ total_transactions = (
+ select count(*) from public.bank_reconciliation_items
+ where session_id = coalesce(new.session_id, old.session_id)
+ )
+ where id = coalesce(new.session_id, old.session_id);
+
+ return coalesce(new, old);
+end;
+$function$;