books/backend/Books.Api/Database/Migrations/005_UserCompanyAccess.sql
Nicolaj Hartmann 1f75c5d791 Add all backend domain, commands, repositories, and tests
This commit includes all previously untracked backend files:

Domain:
- Accounts, Attachments, BankConnections, Customers
- FiscalYears, Invoices, JournalEntryDrafts
- Orders, Products, UserAccess

Commands & Handlers:
- Full CQRS command structure for all domains

Repositories:
- PostgreSQL repositories for all read models
- Bank transaction and ledger repositories

GraphQL:
- Input types, scalars, and types for all entities
- Mutations and queries

Infrastructure:
- Banking integration (Enable Banking client)
- File storage, Invoicing, Reporting, SAF-T export
- Database migrations (003-029)

Tests:
- Integration tests for GraphQL endpoints
- Domain tests
- Invoicing and reporting tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:19:42 +01:00

33 lines
1.6 KiB
SQL

-- User Company Access table for multi-tenant authorization
-- Maps users to companies with specific roles
CREATE TABLE IF NOT EXISTS user_company_access_read_models (
aggregate_id VARCHAR(255) PRIMARY KEY,
create_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_aggregate_sequence_number INT NOT NULL DEFAULT 1,
user_id VARCHAR(255) NOT NULL, -- Keycloak user ID or email
company_id VARCHAR(255) NOT NULL, -- Reference to company aggregate
role VARCHAR(50) NOT NULL, -- owner, accountant, viewer
granted_by VARCHAR(255) NOT NULL, -- Who granted this access
granted_at TIMESTAMPTZ NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
revoked_at TIMESTAMPTZ,
revoked_by VARCHAR(255),
-- Ensure unique user-company combination (only one active access per user per company)
CONSTRAINT uq_user_company_active UNIQUE (user_id, company_id),
-- Ensure valid role values
CONSTRAINT chk_role CHECK (role IN ('owner', 'accountant', 'viewer'))
);
-- Index for looking up all companies a user has access to
CREATE INDEX IF NOT EXISTS idx_user_access_user ON user_company_access_read_models(user_id) WHERE is_active = true;
-- Index for looking up all users with access to a company
CREATE INDEX IF NOT EXISTS idx_user_access_company ON user_company_access_read_models(company_id) WHERE is_active = true;
-- Composite index for efficient access checks
CREATE INDEX IF NOT EXISTS idx_user_access_check ON user_company_access_read_models(user_id, company_id, role) WHERE is_active = true;