use master GO -- ========================================== -- MASTER databáze pro přihlašování a rozřazování -- ========================================== CREATE DATABASE [kufrik_master] -- CONTAINMENT = NONE -- ON PRIMARY --( NAME = N'kufrik', FILENAME = N'/var/opt/mssql/data/kufrik.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB ) -- LOG ON --( NAME = N'kufrik_log', FILENAME = N'/var/opt/mssql/data/kufrik_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB ) GO ALTER DATABASE [kufrik_master] SET COMPATIBILITY_LEVEL = 160 GO -- Pokud je AUTO_CLOSE zapnuté (ON), databáze se po odpojení posledního uživatele kompletně uzavře a uvolní paměť. Při dalším požadavku se pak musí znovu otevírat, což způsobuje výrazné zpoždění (latenci) prvního dotazu a zbytečnou zátěž na disk a CPU. ALTER DATABASE [kufrik_master] SET AUTO_CLOSE OFF WITH NO_WAIT; GO -- user: USE [master] GO CREATE LOGIN [kufrik] WITH PASSWORD=N'giteaKufrik_2026!', DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF GO USE [kufrik_master] GO CREATE USER [kufrik] FOR LOGIN [kufrik] GO ALTER USER [kufrik] WITH DEFAULT_SCHEMA=[dbo] GO ALTER ROLE [db_owner] ADD MEMBER [kufrik] GO -- TABULKA LOGIN IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[LOGIN]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[LOGIN] ( [ID] [int] IDENTITY(1,1) NOT NULL, [EMAIL] [nvarchar](100) NOT NULL, [HESLO_HASH] [nvarchar](255) NOT NULL, [POSLEDNI_PRIHLASENI] [datetime2](7) NULL, [AKTIVNI] [bit] NOT NULL CONSTRAINT [DF_LOGIN_AKTIVNI] DEFAULT 1, [DATABASE] [nvarchar](128) NOT NULL, [SERVER] [nvarchar](100) NOT NULL, [FIRMA_ID] [int] NOT NULL, -- vazna na tabulku FIRMA v datové DB CONSTRAINT [PK_LOGIN] PRIMARY KEY CLUSTERED ([ID] ASC), CONSTRAINT [UQ_LOGIN_EMAIL] UNIQUE NONCLUSTERED ([EMAIL] ASC) ); END GO SET IDENTITY_INSERT dbo.[LOGIN] ON; INSERT INTO dbo.[LOGIN] ( [ID], [EMAIL], [HESLO_HASH], [DATABASE], [SERVER], [FIRMA_ID] ) VALUES ( 1, N'admin@kmetix.cz', -- Heslo 'admin1234' (platný BCrypt hash pro Nette Passwords) N'$2y$12$9SAWctZKp/JpYl7K.awSRehXOXVyRpu0ofReqvbm1jVjvdz41zH7a', N'kufrik', N'localhost', 1 -- Odkaz na FIRMA.ID = 1 v klientské databázi 'kufrik' ); SET IDENTITY_INSERT dbo.[LOGIN] OFF; GO -- ========================================== -- DATOVÁ databáze -- ========================================== CREATE DATABASE [kufrik] -- CONTAINMENT = NONE -- ON PRIMARY --( NAME = N'kufrik', FILENAME = N'/var/opt/mssql/data/kufrik.mdf' , SIZE = 8192KB , FILEGROWTH = 65536KB ) -- LOG ON --( NAME = N'kufrik_log', FILENAME = N'/var/opt/mssql/data/kufrik_log.ldf' , SIZE = 8192KB , FILEGROWTH = 65536KB ) GO ALTER DATABASE [kufrik] SET COMPATIBILITY_LEVEL = 160 GO --Pokud je AUTO_CLOSE zapnuté (ON), databáze se po odpojení posledního uživatele kompletně uzavře a uvolní paměť. Při dalším požadavku se pak musí znovu otevírat, což způsobuje výrazné zpoždění (latenci) prvního dotazu a zbytečnou zátěž na disk a CPU. ALTER DATABASE [kufrik] SET AUTO_CLOSE OFF WITH NO_WAIT; GO --user (stejný i pro master DB): USE [kufrik] GO CREATE USER [kufrik] FOR LOGIN [kufrik] GO ALTER USER [kufrik] WITH DEFAULT_SCHEMA=[dbo] GO ALTER ROLE [db_owner] ADD MEMBER [kufrik] GO -- ========================================== -- 1. FIRMA HLAVNÍ TABULKA - vázaná na LOGIN z master_DB (taky bude tenant - izolace dat) -- ========================================== CREATE TABLE [dbo].[FIRMA] ( [ID] [int] IDENTITY(1,1) NOT NULL, [NAZEV] [nvarchar](255) NOT NULL, -- Jméno a příjmení OSVČ nebo název s.r.o. [IC] [nvarchar](8) NULL, [DIC] [nvarchar](12) NULL, [PLATCE_DPH] [bit] NOT NULL DEFAULT 0, -- Kontaktní údaje [TELEFON] [nvarchar](20) NULL, [EMAIL] [nvarchar](100) NULL, -- Sídlo [ULICE] [nvarchar](100) NULL, [MESTO] [nvarchar](100) NULL, [PSC] [nvarchar](10) NULL, -- Banka pro fakturaci [CISLO_UCTU] [nvarchar](30) NULL, [BANKA_KOD] [nvarchar](10) NULL, [IBAN] [nvarchar](34) NULL, [BIC] [nvarchar](11) NULL, CONSTRAINT [PK_FIRMA] PRIMARY KEY CLUSTERED ([ID] ASC) ); -- ========================================== -- 1. ZÁKAZNÍCI / ODBĚRATELÉ (vázaní na Firmu) -- ========================================== CREATE TABLE [dbo].[ZAKAZNIK]( [ID] [int] IDENTITY(2,1) NOT NULL, [FIRMA_ID] [int] NOT NULL, -- Izolace dat (Tenant) [JMENO] [nvarchar](255) NULL, [PRIJMENI] [nvarchar](255) NOT NULL, [ULICE] [nvarchar](50) NULL, [MESTO] [nvarchar](50) NULL, [PSC] [nvarchar](5) NULL, [IC] [nvarchar](8) NULL, [DIC] [nvarchar](10) NULL, [TELEFON] [nvarchar](15) NULL, [EMAIL] [nvarchar](100) NULL, [AKTIVNI] [bit] NOT NULL CONSTRAINT [DF_ZAKAZNIK_AKTIVNI] DEFAULT ((1)), CONSTRAINT [PK_ZAKAZNIK] PRIMARY KEY CLUSTERED ([ID] ASC), CONSTRAINT [FK_ZAKAZNIK_FIRMA] FOREIGN KEY ([FIRMA_ID]) REFERENCES [dbo].[FIRMA] ([ID]) ); GO CREATE NONCLUSTERED INDEX [IX_ZAKAZNIK_FIRMA_ID] ON [dbo].[ZAKAZNIK]([FIRMA_ID] ASC); GO -- ========================================== -- 2. HLAVIČKA FAKTURY -- ========================================== CREATE TABLE [dbo].[FAKTURA]( [ID] [int] IDENTITY(15,1) NOT NULL, [FIRMA_ID] [int] NOT NULL, -- Izolace dat (Tenant) [ZAKAZNIK] [int] NULL, -- Vazba na adresář (volitelná, pro historii) [CISLO] [nvarchar](20) NOT NULL, [CISLO_ABS] [int] NOT NULL, -- Sekvenční pořadové číslo v roce [FORMA_UHRADY] [tinyint] NOT NULL CONSTRAINT [DF_FAKTURA_FORMA_UHRADY] DEFAULT ((1)), [DATUM_VYSTAVENI] [date] NOT NULL, [DATUM_SPLATNOSTI] [date] NOT NULL, [DATUM_DUZP] [date] NULL, -- Doplněno pro DPH plnění -- Snapshot Dodavatele (OSVČ / Firma v čase vystavení) [D_PRAJM] [nvarchar](511) NULL, [D_ULICE] [nvarchar](50) NULL, [D_MESTO] [nvarchar](50) NULL, [D_PSC] [nvarchar](50) NULL, [D_IC] [nvarchar](8) NULL, [D_DIC] [nvarchar](10) NULL, [D_BANKA] [nvarchar](100) NULL, [D_TELEFON] [nvarchar](15) NULL, [D_EMAIL] [nvarchar](100) NULL, [D_WEB] [nvarchar](100) NULL, -- Snapshot Odběratele (Zákazník v čase vystavení) [O_PRAJM] [nvarchar](511) NULL, [O_ULICE] [nvarchar](50) NULL, [O_MESTO] [nvarchar](50) NULL, [O_PSC] [nvarchar](5) NULL, [O_IC] [nvarchar](8) NULL, [O_DIC] [nvarchar](10) NULL, [CINNOSTI] [nvarchar](max) NULL, -- Nahrazeno ntext -> nvarchar(max) [STAV] [nvarchar](20) NOT NULL CONSTRAINT [DF_FAKTURA_STAV] DEFAULT ('ISSUED'), CONSTRAINT [PK_FAKTURA] PRIMARY KEY CLUSTERED ([ID] ASC), CONSTRAINT [FK_FAKTURA_FIRMA] FOREIGN KEY ([FIRMA_ID]) REFERENCES [dbo].[FIRMA] ([ID]), CONSTRAINT [FK_FAKTURA_ZAKAZNIK] FOREIGN KEY ([ZAKAZNIK]) REFERENCES [dbo].[ZAKAZNIK] ([ID]), -- Číslo faktury je unikátní vždy jen v rámci jedné firmy CONSTRAINT [UQ_FAKTURA_FIRMA_CISLO] UNIQUE ([FIRMA_ID], [CISLO]) ); GO CREATE NONCLUSTERED INDEX [IX_FAKTURA_FIRMA_DATUM] ON [dbo].[FAKTURA]([FIRMA_ID] ASC, [DATUM_VYSTAVENI] DESC); GO -- ========================================== -- 3. POLOŽKY FAKTURY -- ========================================== CREATE TABLE [dbo].[POLOZKA]( [ID] [int] IDENTITY(24,1) NOT NULL, [FAKTURA] [int] NOT NULL, [FIRMA_ID] [int] NOT NULL, -- Duplikováno pro rychlé filtry a RLS [PORADI] [int] NOT NULL CONSTRAINT [DF_POLOZKA_PORADI] DEFAULT (1), -- Pořadí položky na faktuře [NAZEV] [nvarchar](255) NOT NULL, [POCET] [float] NOT NULL DEFAULT 1, [JEDNOTKA] [nvarchar](10) NULL DEFAULT 'ks', [CENA_JEDNOTKA] [money] NOT NULL, [SLEVA] [real] NULL DEFAULT 0, [CENA] [money] NOT NULL, -- Celková cena položky po slevě CONSTRAINT [PK_POLOZKA] PRIMARY KEY CLUSTERED ([ID] ASC), CONSTRAINT [FK_POLOZKA_FAKTURA] FOREIGN KEY ([FAKTURA]) REFERENCES [dbo].[FAKTURA] ([ID]) ON DELETE CASCADE, CONSTRAINT [FK_POLOZKA_FIRMA] FOREIGN KEY ([FIRMA_ID]) REFERENCES [dbo].[FIRMA] ([ID]) ); GO CREATE NONCLUSTERED INDEX [IX_POLOZKA_FAKTURA_PORADI] ON [dbo].[POLOZKA]([FAKTURA] ASC, [PORADI] ASC); GO -- ========================================== -- 4. ÚHRADY / PLATBY FAKTUR -- ========================================== CREATE TABLE [dbo].[PLATBA]( [ID] [int] IDENTITY(11,1) NOT NULL, [FAKTURA] [int] NOT NULL, [CASTKA] [money] NOT NULL, [DATUM] [datetime2](7) NOT NULL DEFAULT SYSDATETIME(), [POZNAMKA] [nvarchar](max) NULL, -- Nahrazeno ntext -> nvarchar(max) CONSTRAINT [PK_PLATBA] PRIMARY KEY CLUSTERED ([ID] ASC), CONSTRAINT [FK_PLATBA_FAKTURA] FOREIGN KEY ([FAKTURA]) REFERENCES [dbo].[FAKTURA] ([ID]) ON DELETE CASCADE ); GO CREATE NONCLUSTERED INDEX [IX_PLATBA_FAKTURA] ON [dbo].[PLATBA]([FAKTURA] ASC); GO -- ========================================== -- 5. SECURITY -- ========================================== -- 1. Vytvoření schématu pro bezpečnostní objekty CREATE SCHEMA Security; GO -- 2. Predikátová funkce pro kontrolu FirmaId CREATE FUNCTION Security.fn_SecurityPredicate(@FirmaId INT) RETURNS TABLE WITH SCHEMABINDING AS RETURN SELECT 1 AS fn_SecurityPredicate_result WHERE -- Vyhodnotí se jako pravda pouze pokud se FIRMA_ID řádku shoduje se SESSION_CONTEXTu @FirmaId = CAST(SESSION_CONTEXT(N'FirmaId') AS INT); GO -- 3. Vytvoření Security Policy pro tabulky CREATE SECURITY POLICY Security.FirmaSecurityPolicy ADD FILTER PREDICATE Security.fn_SecurityPredicate(FIRMA_ID) ON dbo.FAKTURA, ADD BLOCK PREDICATE Security.fn_SecurityPredicate(FIRMA_ID) ON dbo.FAKTURA, ADD FILTER PREDICATE Security.fn_SecurityPredicate(FIRMA_ID) ON dbo.ZAKAZNIK, ADD BLOCK PREDICATE Security.fn_SecurityPredicate(FIRMA_ID) ON dbo.ZAKAZNIK, ADD FILTER PREDICATE Security.fn_SecurityPredicate(FIRMA_ID) ON dbo.POLOZKA, ADD BLOCK PREDICATE Security.fn_SecurityPredicate(FIRMA_ID) ON dbo.POLOZKA WITH (STATE = ON); GO -- ========================================== -- Cvičná data FIRMA -- ========================================== IF NOT EXISTS (SELECT 1 FROM dbo.[FIRMA] WHERE [ID] = 1) BEGIN SET IDENTITY_INSERT dbo.[FIRMA] ON; INSERT INTO dbo.[FIRMA] ( [ID], [NAZEV], [IC], [DIC], [PLATCE_DPH], [TELEFON], [EMAIL], [ULICE], [MESTO], [PSC], [CISLO_UCTU], [BANKA_KOD], [IBAN], [BIC] ) VALUES ( 1, N'Pavel Kowalski - Kmetix Systems', N'12345678', N'CZ12345678', 1, N'+420123456789', N'admin@kmetix.cz', N'Nádražní 123', N'Liberec', N'46001', N'123456789/0800', N'0800', N'CZ6508000000000123456789', N'GIGACZPX' ); SET IDENTITY_INSERT dbo.[FIRMA] OFF; PRINT N'Cvičná firma ID 1 byla úspěšně vložena do klientské DB.'; END GO