🤝 Subunternehmer-Modul: DB-Migration

This commit is contained in:
2026-03-12 16:17:06 +00:00
parent 18d4e70f46
commit a44a848cdf

View File

@@ -0,0 +1,198 @@
-- SeCu Partnerships (Subunternehmer-System)
-- Version: 1.1.0
-- Partnerschaften zwischen Organisationen
CREATE TABLE IF NOT EXISTS org_partnerships (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
-- Hauptunternehmer (der Aufträge vergibt)
contractor_org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
-- Subunternehmer (der Mitarbeiter stellt)
subcontractor_org_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
-- Status der Partnerschaft
status VARCHAR(20) NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'active', 'paused', 'terminated')),
-- Vertragsdetails
contract_start DATE,
contract_end DATE,
notes TEXT,
-- Wer hat eingeladen
invited_by UUID REFERENCES users(id),
accepted_by UUID REFERENCES users(id),
accepted_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
-- Ein Subunternehmer kann nur einmal pro Hauptunternehmer existieren
UNIQUE(contractor_org_id, subcontractor_org_id),
-- Keine Selbst-Partnerschaft
CHECK (contractor_org_id != subcontractor_org_id)
);
-- Stundensätze und Konditionen pro Partnerschaft
CREATE TABLE IF NOT EXISTS partnership_rates (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
partnership_id UUID NOT NULL REFERENCES org_partnerships(id) ON DELETE CASCADE,
-- Art des Satzes
rate_type VARCHAR(50) NOT NULL DEFAULT 'hourly', -- hourly, daily, fixed, per_order
-- Bezeichnung (z.B. "Normaldienst", "Nachtdienst", "Wochenende")
name VARCHAR(100) NOT NULL,
description TEXT,
-- Betrag in Cent (für Genauigkeit)
amount_cents INTEGER NOT NULL,
currency VARCHAR(3) DEFAULT 'EUR',
-- Gültigkeit
valid_from DATE DEFAULT CURRENT_DATE,
valid_until DATE,
-- Standard-Satz?
is_default BOOLEAN DEFAULT false,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Geteilte Aufträge (Hauptunternehmer -> Subunternehmer)
CREATE TABLE IF NOT EXISTS shared_orders (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
partnership_id UUID NOT NULL REFERENCES org_partnerships(id) ON DELETE CASCADE,
-- Original-Auftrag beim Hauptunternehmer
original_order_id UUID NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
-- Kopie/Referenz beim Subunternehmer (optional, wenn sie eigene Aufträge anlegen)
sub_order_id UUID REFERENCES orders(id) ON DELETE SET NULL,
-- Welcher Satz gilt für diesen Auftrag
rate_id UUID REFERENCES partnership_rates(id),
-- Anzahl benötigter Mitarbeiter vom Subunternehmer
required_staff INTEGER DEFAULT 1,
-- Status
status VARCHAR(20) NOT NULL DEFAULT 'requested'
CHECK (status IN ('requested', 'accepted', 'declined', 'completed', 'cancelled')),
-- Notizen zwischen den Unternehmen
contractor_notes TEXT,
subcontractor_notes TEXT,
responded_at TIMESTAMP WITH TIME ZONE,
completed_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Stundenzettel von Subunternehmer-Mitarbeitern für geteilte Aufträge
CREATE TABLE IF NOT EXISTS partnership_timesheets (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
shared_order_id UUID NOT NULL REFERENCES shared_orders(id) ON DELETE CASCADE,
-- Original-Stundenzettel beim Subunternehmer
timesheet_id UUID NOT NULL REFERENCES timesheets(id) ON DELETE CASCADE,
-- Welcher Satz wurde angewendet
rate_id UUID REFERENCES partnership_rates(id),
-- Berechneter Betrag
calculated_amount_cents INTEGER,
-- Status der Freigabe durch Hauptunternehmer
approval_status VARCHAR(20) DEFAULT 'pending'
CHECK (approval_status IN ('pending', 'approved', 'disputed', 'rejected')),
approved_by UUID REFERENCES users(id),
approved_at TIMESTAMP WITH TIME ZONE,
dispute_reason TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Abrechnungen zwischen Partnern
CREATE TABLE IF NOT EXISTS partnership_invoices (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
partnership_id UUID NOT NULL REFERENCES org_partnerships(id) ON DELETE CASCADE,
-- Rechnungsnummer
invoice_number VARCHAR(50) NOT NULL,
-- Abrechnungszeitraum
period_start DATE NOT NULL,
period_end DATE NOT NULL,
-- Beträge
subtotal_cents INTEGER NOT NULL DEFAULT 0,
tax_percent DECIMAL(5,2) DEFAULT 19.00,
tax_cents INTEGER NOT NULL DEFAULT 0,
total_cents INTEGER NOT NULL DEFAULT 0,
-- Status
status VARCHAR(20) NOT NULL DEFAULT 'draft'
CHECK (status IN ('draft', 'sent', 'paid', 'overdue', 'cancelled')),
-- Daten
issued_at TIMESTAMP WITH TIME ZONE,
due_date DATE,
paid_at TIMESTAMP WITH TIME ZONE,
-- PDF-Link (wenn generiert)
pdf_url TEXT,
-- Notizen
notes TEXT,
created_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Rechnungspositionen
CREATE TABLE IF NOT EXISTS partnership_invoice_items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
invoice_id UUID NOT NULL REFERENCES partnership_invoices(id) ON DELETE CASCADE,
-- Referenz zum Stundenzettel (optional)
partnership_timesheet_id UUID REFERENCES partnership_timesheets(id),
-- Beschreibung
description TEXT NOT NULL,
-- Mengen und Preise
quantity DECIMAL(10,2) NOT NULL DEFAULT 1,
unit VARCHAR(20) DEFAULT 'Stunden',
unit_price_cents INTEGER NOT NULL,
total_cents INTEGER NOT NULL,
-- Datum der Leistung
service_date DATE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
-- Indexes
CREATE INDEX IF NOT EXISTS idx_partnerships_contractor ON org_partnerships(contractor_org_id);
CREATE INDEX IF NOT EXISTS idx_partnerships_subcontractor ON org_partnerships(subcontractor_org_id);
CREATE INDEX IF NOT EXISTS idx_partnerships_status ON org_partnerships(status);
CREATE INDEX IF NOT EXISTS idx_shared_orders_partnership ON shared_orders(partnership_id);
CREATE INDEX IF NOT EXISTS idx_shared_orders_original ON shared_orders(original_order_id);
CREATE INDEX IF NOT EXISTS idx_partnership_timesheets_shared ON partnership_timesheets(shared_order_id);
CREATE INDEX IF NOT EXISTS idx_partnership_invoices_partnership ON partnership_invoices(partnership_id);
CREATE INDEX IF NOT EXISTS idx_partnership_invoices_status ON partnership_invoices(status);
-- Partnership-Modul registrieren
INSERT INTO modules (name, display_name, description, is_core, default_config) VALUES
('partnerships', 'Subunternehmer', 'Zusammenarbeit mit Subunternehmen inkl. Abrechnung', false,
'{"auto_approve_timesheets": false, "default_payment_days": 30}')
ON CONFLICT (name) DO NOTHING;