refactor: clean up codebase, remove dead code and obsolete docs
Remove influencer-era documentation, unused components, boilerplate assets, and ghost tiktok cron job. Add supplier invoice management, document API routes, and PWA icons. Replace boilerplate README. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
e8743e6e03
commit
838dc6b8b5
@@ -260,10 +260,14 @@ begin
|
||||
insert into public.chart_of_accounts (user_id, account_number, account_name, account_class, account_group, account_type, normal_balance, plan_type, is_system_account, sru_code)
|
||||
values
|
||||
(p_user_id, '2440', 'Leverantorsskulder', 2, '24', 'liability', 'credit', 'k1', true, '7230'),
|
||||
(p_user_id, '2610', 'Utgaende moms 25%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2611', 'Utgaende moms 12%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2612', 'Utgaende moms 6%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2611', 'Utgaende moms forsaljning 25%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2621', 'Utgaende moms forsaljning 12%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2631', 'Utgaende moms forsaljning 6%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2614', 'Utg moms omvand skattskyldighet 25%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2624', 'Utg moms omvand skattskyldighet 12%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2634', 'Utg moms omvand skattskyldighet 6%', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2641', 'Debiterad ingaende moms', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2645', 'Beraknad ingaende moms forvarv utlandet', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2650', 'Redovisningskonto for moms', 2, '26', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2710', 'Personalskatt', 2, '27', 'liability', 'credit', 'k1', true, '7231'),
|
||||
(p_user_id, '2731', 'Avrakning socialavgifter', 2, '27', 'liability', 'credit', 'k1', true, '7231');
|
||||
@@ -282,7 +286,10 @@ begin
|
||||
values
|
||||
(p_user_id, '3001', 'Forsaljning tjanster 25%', 3, '30', 'revenue', 'credit', 'k1', true, '7310'),
|
||||
(p_user_id, '3002', 'Forsaljning varor 25%', 3, '30', 'revenue', 'credit', 'k1', true, '7310'),
|
||||
(p_user_id, '3003', 'Forsaljning tjanster 6%', 3, '30', 'revenue', 'credit', 'k1', true, '7310'),
|
||||
(p_user_id, '3100', 'Momsfri forsaljning', 3, '31', 'revenue', 'credit', 'k1', true, '7311'),
|
||||
(p_user_id, '3305', 'Forsaljning tjanst export', 3, '33', 'revenue', 'credit', 'k1', true, '7310'),
|
||||
(p_user_id, '3308', 'Forsaljning tjanst EU', 3, '33', 'revenue', 'credit', 'k1', true, '7310'),
|
||||
(p_user_id, '3900', 'Ovriga rorelseintakter', 3, '39', 'revenue', 'credit', 'k1', true, '7311'),
|
||||
(p_user_id, '3960', 'Valutakursvinster', 3, '39', 'revenue', 'credit', 'k1', true, '7310');
|
||||
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
-- Migration 23: Document Version Chain Hardening
|
||||
-- Adds cryptographic hash chain + atomic versioning via RPC
|
||||
-- Resolves: Gap 4 (no hash-chain) + Gap 5 (race condition in versioning)
|
||||
|
||||
-- =============================================================================
|
||||
-- 1. New columns on document_attachments
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE public.document_attachments
|
||||
ADD COLUMN IF NOT EXISTS prev_version_hash text,
|
||||
ADD COLUMN IF NOT EXISTS last_integrity_check_at timestamptz;
|
||||
|
||||
COMMENT ON COLUMN public.document_attachments.prev_version_hash IS
|
||||
'SHA-256 hash of the previous version. NULL for version 1 (genesis). Creates tamper-evident chain.';
|
||||
|
||||
COMMENT ON COLUMN public.document_attachments.last_integrity_check_at IS
|
||||
'Timestamp of the last batch integrity verification (set by cron job).';
|
||||
|
||||
-- Index for cron job: prioritize documents never checked or least recently checked
|
||||
CREATE INDEX IF NOT EXISTS idx_doc_attachments_integrity_check
|
||||
ON public.document_attachments (last_integrity_check_at NULLS FIRST)
|
||||
WHERE is_current_version = true;
|
||||
|
||||
-- =============================================================================
|
||||
-- 2. RPC: create_document_version (atomic versioning with row-level lock)
|
||||
-- Pattern follows next_voucher_number() from migration 016
|
||||
-- =============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.create_document_version(
|
||||
p_user_id uuid,
|
||||
p_original_doc_id uuid,
|
||||
p_storage_path text,
|
||||
p_file_name text,
|
||||
p_file_size_bytes bigint,
|
||||
p_mime_type text,
|
||||
p_sha256_hash text,
|
||||
p_upload_source text DEFAULT NULL
|
||||
)
|
||||
RETURNS uuid
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
AS $$
|
||||
DECLARE
|
||||
v_current RECORD;
|
||||
v_root_original_id uuid;
|
||||
v_new_version integer;
|
||||
v_new_id uuid;
|
||||
BEGIN
|
||||
-- Lock the current version row (prevents concurrent versioning)
|
||||
SELECT id, version, sha256_hash, original_id, journal_entry_id,
|
||||
journal_entry_line_id, upload_source
|
||||
INTO v_current
|
||||
FROM public.document_attachments
|
||||
WHERE id = p_original_doc_id
|
||||
AND user_id = p_user_id
|
||||
AND is_current_version = true
|
||||
FOR UPDATE;
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION 'Document not found, not owned by user, or not the current version';
|
||||
END IF;
|
||||
|
||||
v_root_original_id := COALESCE(v_current.original_id, v_current.id);
|
||||
v_new_version := v_current.version + 1;
|
||||
|
||||
-- Insert new version with hash chain link
|
||||
INSERT INTO public.document_attachments (
|
||||
user_id,
|
||||
storage_path,
|
||||
file_name,
|
||||
file_size_bytes,
|
||||
mime_type,
|
||||
sha256_hash,
|
||||
version,
|
||||
original_id,
|
||||
is_current_version,
|
||||
uploaded_by,
|
||||
upload_source,
|
||||
digitization_date,
|
||||
journal_entry_id,
|
||||
journal_entry_line_id,
|
||||
prev_version_hash
|
||||
) VALUES (
|
||||
p_user_id,
|
||||
p_storage_path,
|
||||
p_file_name,
|
||||
p_file_size_bytes,
|
||||
p_mime_type,
|
||||
p_sha256_hash,
|
||||
v_new_version,
|
||||
v_root_original_id,
|
||||
true,
|
||||
p_user_id,
|
||||
COALESCE(p_upload_source, v_current.upload_source),
|
||||
now(),
|
||||
v_current.journal_entry_id,
|
||||
v_current.journal_entry_line_id,
|
||||
v_current.sha256_hash -- cryptographic link to previous version
|
||||
)
|
||||
RETURNING id INTO v_new_id;
|
||||
|
||||
-- Mark old version as superseded (within same transaction = atomic)
|
||||
UPDATE public.document_attachments
|
||||
SET is_current_version = false,
|
||||
superseded_by_id = v_new_id
|
||||
WHERE id = v_current.id;
|
||||
|
||||
RETURN v_new_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.create_document_version(uuid, uuid, text, text, bigint, text, text, text) TO authenticated;
|
||||
|
||||
-- =============================================================================
|
||||
-- 3. RPC: validate_version_chain
|
||||
-- Verifies that prev_version_hash matches sha256_hash of the preceding version
|
||||
-- =============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.validate_version_chain(
|
||||
p_user_id uuid,
|
||||
p_original_doc_id uuid
|
||||
)
|
||||
RETURNS TABLE (
|
||||
doc_id uuid,
|
||||
version integer,
|
||||
sha256_hash text,
|
||||
prev_version_hash text,
|
||||
expected_prev_hash text,
|
||||
chain_valid boolean
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
WITH chain AS (
|
||||
SELECT
|
||||
da.id,
|
||||
da.version,
|
||||
da.sha256_hash AS current_hash,
|
||||
da.prev_version_hash AS stored_prev_hash,
|
||||
LAG(da.sha256_hash) OVER (ORDER BY da.version) AS computed_prev_hash
|
||||
FROM public.document_attachments da
|
||||
WHERE da.user_id = p_user_id
|
||||
AND (da.id = p_original_doc_id OR da.original_id = p_original_doc_id)
|
||||
ORDER BY da.version
|
||||
)
|
||||
SELECT
|
||||
chain.id AS doc_id,
|
||||
chain.version,
|
||||
chain.current_hash AS sha256_hash,
|
||||
chain.stored_prev_hash AS prev_version_hash,
|
||||
chain.computed_prev_hash AS expected_prev_hash,
|
||||
CASE
|
||||
WHEN chain.version = 1 THEN (chain.stored_prev_hash IS NULL)
|
||||
ELSE (chain.stored_prev_hash IS NOT NULL AND chain.stored_prev_hash = chain.computed_prev_hash)
|
||||
END AS chain_valid
|
||||
FROM chain
|
||||
ORDER BY chain.version;
|
||||
END;
|
||||
$$;
|
||||
|
||||
GRANT EXECUTE ON FUNCTION public.validate_version_chain(uuid, uuid) TO authenticated;
|
||||
@@ -0,0 +1,57 @@
|
||||
-- Migration 24: Storage Bucket Policies for WORM Document Archive
|
||||
-- Resolves: Gap 3 (no immutability on Storage level)
|
||||
--
|
||||
-- Creates a private 'documents' bucket with:
|
||||
-- - INSERT policy (authenticated users upload to their own folder)
|
||||
-- - SELECT policy (authenticated users read their own documents)
|
||||
-- - NO UPDATE policy (prevents overwriting files — WORM)
|
||||
-- - NO DELETE policy (prevents client-side deletion — WORM)
|
||||
--
|
||||
-- Service role (cron, admin) bypasses RLS and can still access everything.
|
||||
|
||||
-- =============================================================================
|
||||
-- 1. Create the 'documents' bucket (private, 50MB file limit)
|
||||
-- =============================================================================
|
||||
|
||||
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
|
||||
VALUES (
|
||||
'documents',
|
||||
'documents',
|
||||
false,
|
||||
52428800, -- 50 MB
|
||||
NULL -- Allow all MIME types (PDFs, images, etc.)
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- =============================================================================
|
||||
-- 2. INSERT policy: Authenticated users can upload to their own folder
|
||||
-- =============================================================================
|
||||
|
||||
CREATE POLICY "documents_insert_own"
|
||||
ON storage.objects
|
||||
FOR INSERT
|
||||
TO authenticated
|
||||
WITH CHECK (
|
||||
bucket_id = 'documents'
|
||||
AND (storage.foldername(name))[1] = 'documents'
|
||||
AND (storage.foldername(name))[2] = auth.uid()::text
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- 3. SELECT policy: Authenticated users can read their own documents
|
||||
-- =============================================================================
|
||||
|
||||
CREATE POLICY "documents_select_own"
|
||||
ON storage.objects
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
bucket_id = 'documents'
|
||||
AND (storage.foldername(name))[1] = 'documents'
|
||||
AND (storage.foldername(name))[2] = auth.uid()::text
|
||||
);
|
||||
|
||||
-- =============================================================================
|
||||
-- No UPDATE or DELETE policies — WORM compliance
|
||||
-- Service role bypasses RLS for admin/cron access
|
||||
-- =============================================================================
|
||||
@@ -0,0 +1,342 @@
|
||||
-- Migration 25: Leverantorsreskontra (Accounts Payable)
|
||||
-- Suppliers, supplier invoices, line items, partial payments
|
||||
-- BFL 5:6-9 verifikationskrav, ML 17:24 inkommande fakturor
|
||||
|
||||
-- =============================================================================
|
||||
-- 1. suppliers table
|
||||
-- =============================================================================
|
||||
CREATE TABLE IF NOT EXISTS public.suppliers (
|
||||
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
|
||||
-- Basic info
|
||||
name text NOT NULL,
|
||||
supplier_type text NOT NULL DEFAULT 'swedish_business'
|
||||
CHECK (supplier_type IN ('swedish_business', 'eu_business', 'non_eu_business')),
|
||||
org_number text,
|
||||
vat_number text,
|
||||
|
||||
-- Contact
|
||||
email text,
|
||||
phone text,
|
||||
|
||||
-- Address
|
||||
address_line1 text,
|
||||
address_line2 text,
|
||||
postal_code text,
|
||||
city text,
|
||||
country text NOT NULL DEFAULT 'SE',
|
||||
|
||||
-- Payment details
|
||||
bankgiro text,
|
||||
plusgiro text,
|
||||
bank_account text,
|
||||
iban text,
|
||||
bic text,
|
||||
|
||||
-- Defaults
|
||||
default_expense_account text, -- e.g. '5010'
|
||||
default_payment_terms integer NOT NULL DEFAULT 30,
|
||||
default_currency text NOT NULL DEFAULT 'SEK',
|
||||
|
||||
-- Notes
|
||||
notes text,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
ALTER TABLE public.suppliers ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Users can view own suppliers"
|
||||
ON public.suppliers FOR SELECT
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can insert own suppliers"
|
||||
ON public.suppliers FOR INSERT
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can update own suppliers"
|
||||
ON public.suppliers FOR UPDATE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can delete own suppliers"
|
||||
ON public.suppliers FOR DELETE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE INDEX idx_suppliers_user_id ON public.suppliers (user_id);
|
||||
CREATE INDEX idx_suppliers_name ON public.suppliers (user_id, name);
|
||||
|
||||
-- =============================================================================
|
||||
-- 2. supplier_invoices table
|
||||
-- =============================================================================
|
||||
CREATE TABLE IF NOT EXISTS public.supplier_invoices (
|
||||
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
supplier_id uuid NOT NULL REFERENCES public.suppliers(id) ON DELETE RESTRICT,
|
||||
|
||||
-- Ankomstnummer (BFL running number for incoming invoices)
|
||||
arrival_number integer NOT NULL,
|
||||
|
||||
-- Supplier's reference
|
||||
supplier_invoice_number text NOT NULL,
|
||||
|
||||
-- Dates (ML 17:24 requirements)
|
||||
invoice_date date NOT NULL,
|
||||
due_date date NOT NULL,
|
||||
received_date date NOT NULL DEFAULT CURRENT_DATE,
|
||||
delivery_date date, -- ML krav
|
||||
|
||||
-- Status
|
||||
status text NOT NULL DEFAULT 'registered'
|
||||
CHECK (status IN ('registered', 'approved', 'paid', 'partially_paid', 'overdue', 'disputed', 'credited')),
|
||||
|
||||
-- Currency
|
||||
currency text NOT NULL DEFAULT 'SEK',
|
||||
exchange_rate numeric,
|
||||
exchange_rate_date date,
|
||||
|
||||
-- Amounts
|
||||
subtotal numeric NOT NULL DEFAULT 0,
|
||||
subtotal_sek numeric,
|
||||
vat_amount numeric NOT NULL DEFAULT 0,
|
||||
vat_amount_sek numeric,
|
||||
total numeric NOT NULL DEFAULT 0,
|
||||
total_sek numeric,
|
||||
|
||||
-- VAT
|
||||
vat_treatment text NOT NULL DEFAULT 'standard_25',
|
||||
reverse_charge boolean NOT NULL DEFAULT false,
|
||||
|
||||
-- Payment
|
||||
payment_reference text, -- OCR-nummer
|
||||
paid_at timestamptz,
|
||||
paid_amount numeric NOT NULL DEFAULT 0,
|
||||
remaining_amount numeric NOT NULL DEFAULT 0,
|
||||
|
||||
-- Credit note
|
||||
is_credit_note boolean NOT NULL DEFAULT false,
|
||||
credited_invoice_id uuid REFERENCES public.supplier_invoices(id) ON DELETE SET NULL,
|
||||
|
||||
-- Bookkeeping links
|
||||
registration_journal_entry_id uuid REFERENCES public.journal_entries(id) ON DELETE SET NULL,
|
||||
payment_journal_entry_id uuid REFERENCES public.journal_entries(id) ON DELETE SET NULL,
|
||||
|
||||
-- Transaction link
|
||||
transaction_id uuid REFERENCES public.transactions(id) ON DELETE SET NULL,
|
||||
|
||||
-- Document (verifikationsunderlag)
|
||||
document_id uuid REFERENCES public.document_attachments(id) ON DELETE SET NULL,
|
||||
|
||||
-- Notes
|
||||
notes text,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT uq_supplier_invoices_arrival UNIQUE (user_id, arrival_number),
|
||||
CONSTRAINT uq_supplier_invoices_ref UNIQUE (user_id, supplier_id, supplier_invoice_number)
|
||||
);
|
||||
|
||||
ALTER TABLE public.supplier_invoices ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Users can view own supplier invoices"
|
||||
ON public.supplier_invoices FOR SELECT
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can insert own supplier invoices"
|
||||
ON public.supplier_invoices FOR INSERT
|
||||
WITH CHECK (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can update own supplier invoices"
|
||||
ON public.supplier_invoices FOR UPDATE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE POLICY "Users can delete own supplier invoices"
|
||||
ON public.supplier_invoices FOR DELETE
|
||||
USING (auth.uid() = user_id);
|
||||
|
||||
CREATE INDEX idx_supplier_invoices_user_id ON public.supplier_invoices (user_id);
|
||||
CREATE INDEX idx_supplier_invoices_supplier ON public.supplier_invoices (supplier_id);
|
||||
CREATE INDEX idx_supplier_invoices_status ON public.supplier_invoices (user_id, status);
|
||||
CREATE INDEX idx_supplier_invoices_due_date ON public.supplier_invoices (user_id, due_date);
|
||||
|
||||
-- =============================================================================
|
||||
-- 3. Per-user arrival number generation (ankomstnummer)
|
||||
-- =============================================================================
|
||||
CREATE OR REPLACE FUNCTION public.get_next_arrival_number(p_user_id uuid)
|
||||
RETURNS integer AS $$
|
||||
DECLARE
|
||||
next_num integer;
|
||||
BEGIN
|
||||
SELECT COALESCE(MAX(arrival_number), 0) + 1 INTO next_num
|
||||
FROM public.supplier_invoices
|
||||
WHERE user_id = p_user_id;
|
||||
RETURN next_num;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- =============================================================================
|
||||
-- 4. supplier_invoice_items table
|
||||
-- =============================================================================
|
||||
CREATE TABLE IF NOT EXISTS public.supplier_invoice_items (
|
||||
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
supplier_invoice_id uuid NOT NULL REFERENCES public.supplier_invoices(id) ON DELETE CASCADE,
|
||||
|
||||
sort_order integer NOT NULL DEFAULT 0,
|
||||
description text NOT NULL,
|
||||
quantity numeric NOT NULL DEFAULT 1,
|
||||
unit text NOT NULL DEFAULT 'st',
|
||||
unit_price numeric NOT NULL DEFAULT 0, -- exkl. moms (ML krav)
|
||||
line_total numeric NOT NULL DEFAULT 0,
|
||||
|
||||
-- Bookkeeping
|
||||
account_number text NOT NULL, -- BAS expense account (4xxx-6xxx)
|
||||
vat_code text,
|
||||
vat_rate numeric NOT NULL DEFAULT 0.25,
|
||||
vat_amount numeric NOT NULL DEFAULT 0,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
ALTER TABLE public.supplier_invoice_items ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Users can view own supplier invoice items"
|
||||
ON public.supplier_invoice_items FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.supplier_invoices si
|
||||
WHERE si.id = supplier_invoice_id AND si.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can insert own supplier invoice items"
|
||||
ON public.supplier_invoice_items FOR INSERT
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.supplier_invoices si
|
||||
WHERE si.id = supplier_invoice_id AND si.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can update own supplier invoice items"
|
||||
ON public.supplier_invoice_items FOR UPDATE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.supplier_invoices si
|
||||
WHERE si.id = supplier_invoice_id AND si.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can delete own supplier invoice items"
|
||||
ON public.supplier_invoice_items FOR DELETE
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.supplier_invoices si
|
||||
WHERE si.id = supplier_invoice_id AND si.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_supplier_invoice_items_invoice ON public.supplier_invoice_items (supplier_invoice_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- 5. supplier_invoice_payments table (partial payments)
|
||||
-- =============================================================================
|
||||
CREATE TABLE IF NOT EXISTS public.supplier_invoice_payments (
|
||||
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
supplier_invoice_id uuid NOT NULL REFERENCES public.supplier_invoices(id) ON DELETE CASCADE,
|
||||
|
||||
payment_date date NOT NULL,
|
||||
amount numeric NOT NULL,
|
||||
currency text NOT NULL DEFAULT 'SEK',
|
||||
exchange_rate numeric,
|
||||
exchange_rate_difference numeric DEFAULT 0, -- kursdifferens
|
||||
|
||||
journal_entry_id uuid REFERENCES public.journal_entries(id) ON DELETE SET NULL,
|
||||
transaction_id uuid REFERENCES public.transactions(id) ON DELETE SET NULL,
|
||||
notes text,
|
||||
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
ALTER TABLE public.supplier_invoice_payments ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "Users can view own supplier invoice payments"
|
||||
ON public.supplier_invoice_payments FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.supplier_invoices si
|
||||
WHERE si.id = supplier_invoice_id AND si.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Users can insert own supplier invoice payments"
|
||||
ON public.supplier_invoice_payments FOR INSERT
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM public.supplier_invoices si
|
||||
WHERE si.id = supplier_invoice_id AND si.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_supplier_invoice_payments_invoice ON public.supplier_invoice_payments (supplier_invoice_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- 6. Add supplier_invoice_id to transactions
|
||||
-- =============================================================================
|
||||
ALTER TABLE public.transactions
|
||||
ADD COLUMN IF NOT EXISTS supplier_invoice_id uuid REFERENCES public.supplier_invoices(id) ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_transactions_supplier_invoice ON public.transactions (supplier_invoice_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- 7. Expand journal_entries source_type constraint
|
||||
-- =============================================================================
|
||||
ALTER TABLE public.journal_entries
|
||||
DROP CONSTRAINT IF EXISTS journal_entries_source_type_check;
|
||||
|
||||
ALTER TABLE public.journal_entries
|
||||
ADD CONSTRAINT journal_entries_source_type_check
|
||||
CHECK (source_type IN (
|
||||
'manual', 'bank_transaction', 'invoice_created',
|
||||
'invoice_paid', 'invoice_cash_payment', 'credit_note', 'salary_payment',
|
||||
'opening_balance', 'year_end',
|
||||
'storno', 'correction', 'import', 'system',
|
||||
'supplier_invoice_registered', 'supplier_invoice_paid',
|
||||
'supplier_invoice_cash_payment', 'supplier_credit_note'
|
||||
));
|
||||
|
||||
-- =============================================================================
|
||||
-- 8. Audit triggers for new tables
|
||||
-- =============================================================================
|
||||
CREATE TRIGGER audit_suppliers
|
||||
AFTER INSERT OR UPDATE OR DELETE ON public.suppliers
|
||||
FOR EACH ROW EXECUTE FUNCTION public.write_audit_log();
|
||||
|
||||
CREATE TRIGGER audit_supplier_invoices
|
||||
AFTER INSERT OR UPDATE OR DELETE ON public.supplier_invoices
|
||||
FOR EACH ROW EXECUTE FUNCTION public.write_audit_log();
|
||||
|
||||
CREATE TRIGGER audit_supplier_invoice_items
|
||||
AFTER INSERT OR UPDATE OR DELETE ON public.supplier_invoice_items
|
||||
FOR EACH ROW EXECUTE FUNCTION public.write_audit_log();
|
||||
|
||||
-- =============================================================================
|
||||
-- 9. Updated_at triggers
|
||||
-- =============================================================================
|
||||
CREATE OR REPLACE FUNCTION public.update_updated_at_column()
|
||||
RETURNS trigger AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER set_updated_at_suppliers
|
||||
BEFORE UPDATE ON public.suppliers
|
||||
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER set_updated_at_supplier_invoices
|
||||
BEFORE UPDATE ON public.supplier_invoices
|
||||
FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
|
||||
@@ -0,0 +1,20 @@
|
||||
-- Drop 14 restaurant module tables — zero code references, zero rows.
|
||||
-- These are industry-specific tables that don't belong in base ERP.
|
||||
|
||||
-- Children first
|
||||
DROP TABLE IF EXISTS public.recipe_ingredients CASCADE;
|
||||
DROP TABLE IF EXISTS public.supplier_order_items CASCADE;
|
||||
DROP TABLE IF EXISTS public.menu_items CASCADE;
|
||||
DROP TABLE IF EXISTS public.menu_categories CASCADE;
|
||||
DROP TABLE IF EXISTS public.reservations CASCADE;
|
||||
DROP TABLE IF EXISTS public.shifts CASCADE;
|
||||
|
||||
-- Parents / standalone
|
||||
DROP TABLE IF EXISTS public.menus CASCADE;
|
||||
DROP TABLE IF EXISTS public.recipes CASCADE;
|
||||
DROP TABLE IF EXISTS public.ingredients CASCADE;
|
||||
DROP TABLE IF EXISTS public.staff_members CASCADE;
|
||||
DROP TABLE IF EXISTS public.supplier_orders CASCADE;
|
||||
DROP TABLE IF EXISTS public.restaurant_tables CASCADE;
|
||||
DROP TABLE IF EXISTS public.waste_entries CASCADE;
|
||||
DROP TABLE IF EXISTS public.restaurant_capacity CASCADE;
|
||||
@@ -0,0 +1,58 @@
|
||||
-- Drop 36 unused tables — zero .from() calls, zero rows.
|
||||
-- Grouped by module; children before parents.
|
||||
|
||||
-- HR / Payroll
|
||||
DROP TABLE IF EXISTS public.salary_additions CASCADE;
|
||||
DROP TABLE IF EXISTS public.salary_run_items CASCADE;
|
||||
DROP TABLE IF EXISTS public.salary_payments CASCADE;
|
||||
DROP TABLE IF EXISTS public.salary_runs CASCADE;
|
||||
DROP TABLE IF EXISTS public.absence_records CASCADE;
|
||||
DROP TABLE IF EXISTS public.mileage_entries CASCADE;
|
||||
DROP TABLE IF EXISTS public.employees CASCADE;
|
||||
|
||||
-- Asset Management
|
||||
DROP TABLE IF EXISTS public.depreciation_schedule CASCADE;
|
||||
DROP TABLE IF EXISTS public.assets CASCADE;
|
||||
DROP TABLE IF EXISTS public.asset_categories CASCADE;
|
||||
|
||||
-- Bank Reconciliation
|
||||
DROP TABLE IF EXISTS public.bank_reconciliation_items CASCADE;
|
||||
DROP TABLE IF EXISTS public.bank_reconciliation_sessions CASCADE;
|
||||
DROP TABLE IF EXISTS public.account_balances CASCADE;
|
||||
|
||||
-- Budgeting / Forecasting
|
||||
DROP TABLE IF EXISTS public.budget_entries CASCADE;
|
||||
DROP TABLE IF EXISTS public.budgets CASCADE;
|
||||
DROP TABLE IF EXISTS public.cash_flow_forecasts CASCADE;
|
||||
|
||||
-- Orders / Quotes
|
||||
DROP TABLE IF EXISTS public.order_items CASCADE;
|
||||
DROP TABLE IF EXISTS public.orders CASCADE;
|
||||
DROP TABLE IF EXISTS public.quote_items CASCADE;
|
||||
DROP TABLE IF EXISTS public.quotes CASCADE;
|
||||
|
||||
-- Analytics / KPI
|
||||
DROP TABLE IF EXISTS public.ai_insights_cache CASCADE;
|
||||
DROP TABLE IF EXISTS public.financial_insights CASCADE;
|
||||
DROP TABLE IF EXISTS public.kpi_snapshots CASCADE;
|
||||
|
||||
-- Tax / Compliance
|
||||
DROP TABLE IF EXISTS public.agi_declarations CASCADE;
|
||||
DROP TABLE IF EXISTS public.annual_reports CASCADE;
|
||||
DROP TABLE IF EXISTS public.tax_rates CASCADE;
|
||||
DROP TABLE IF EXISTS public.year_end_closings CASCADE;
|
||||
|
||||
-- Supplier Payments
|
||||
DROP TABLE IF EXISTS public.supplier_payment_items CASCADE;
|
||||
DROP TABLE IF EXISTS public.supplier_payments CASCADE;
|
||||
|
||||
-- Module System
|
||||
DROP TABLE IF EXISTS public.module_configs CASCADE;
|
||||
DROP TABLE IF EXISTS public.module_imports CASCADE;
|
||||
DROP TABLE IF EXISTS public.module_kpi_targets CASCADE;
|
||||
|
||||
-- Other
|
||||
DROP TABLE IF EXISTS public.onboarding_checklist CASCADE;
|
||||
DROP TABLE IF EXISTS public.payment_methods CASCADE;
|
||||
DROP TABLE IF EXISTS public.recurring_invoices CASCADE;
|
||||
DROP TABLE IF EXISTS public.supplier_invoice_attestations CASCADE;
|
||||
Reference in New Issue
Block a user