---
# System prepended metadata

title: Bazy danych - Projekt

---

# <center>Autorzy<br></center>
## <center>Dominik Motyl, Mikołaj Kraczek,<br> Tomasz Leniart , Michał Zięba</center>


## Tabele
- Accounts
- Attractions
- Carriers
- Cities
- CompanyClients
- Countries
- Departments
- Employees
- Offers
- Participants
- Passwords
- Payments
- PersonalClients
- Reservations
- ReservedAttractions
- Tours


## Użytkownicy i ich uprawnienia
### Konsultant
- Przeglądnie ofert
- Tworzenie kont
- Edycja konta
- Dokonywanie rezerwacji
- Anulowanie rezerwacji
- Dodawanie/usuwanie uczestników wycieczki do tygodnia przed
- Dodawanie płatności
```
grant select on dbo.Offers to consultant
grant select on dbo.Tours to consultant
grant insert, update on dbo.Accounts to consultant
grant insert, update on dbo.[Personal Clients] to consultant
grant insert, update on dbo.[Company Clients] to consultant
grant insert, delete on dbo.Reservations to consultant
grant insert on dbo.Payments to consultant
grant select on dbo.Reservations_view to consultant
grant select on dbo.Personal_clients_reservation to consultant
grant select on dbo.Company_clients_reservation to consultant
grant select on dbo.Payment_per_reservation_detalis to consultant
grant select on dbo.tour_view to consultant
```
### Pracownik biura
- Generowanie raportów dla innych osób
- Sprawdzanie poprawności rezerwacji
- Dodawnie wycieczek
- Dodawanie atrakcji do wycieczek
- Ustawianie/dodawanie parametrów wycieczki(transport/miejsce)
- Dodawanie przewoźników
- Dodawanie dostępnych miast
- Dodawanie dostępnych hoteli dla danego miasta
- Dodawanie obsługiwanych krajów
- Ustawianie kosztu wycieczki
```
grant select on database :: u_mizieba to manager
deny select on dbo.Passwords to manager
grant insert on dbo.Attractions to manager
grant insert on dbo.[Tour Attractions] to manager
grant insert on dbo.Tours to manager
grant insert on dbo.Carriers to manager
grant insert on dbo.Cities to manager
grant insert on dbo.Countries to manager
grant insert on dbo.Offers to manager
```
### Administrator bazy
- Tworzenie/testowanie backupów
- Pełna kontrola nad bazą danych
```
grant control, alter on database :: u_mizieba to admin
```


## Funkcjonalności
Zarządzanie rezerwacją odbywa się na naszej stronie po zalogowaniu na wcześniej utworzone konto. Można tam dodawać/usuwać/modyfikować uczestników i dodawać/usuwać atrakcje. Możliwe jest też z tego widoku anulowanie rezerwacji lub zmiany terminów wycieczki.

Konto może zostać utworzone przez osobę prywatną jak i firmę. Każde konto zawierało będzie te same kolumny w tabeli kont, jednak dla osoby prywatnej pola typowe dla firmy, takie jak NIP, będą wartością NULL.

Usuwanie uczestników odbywa się przez zmianę wartości w odpowiednim polu-fladze 

Po rezerwacji otrzymuje się maila, na którego trzeba odpowiedzieć ( np. kliknąć w coś) aby potwierdzić rezerwację. W przypadku braku potwierdzenia rekord jest kasowany po tygodniu. 

Do utworzenia rezerwacji wymagane jest logowanie/utworzenie konta w naszym serwisie.

Po dokonaniu rezerwacji nie jest wymagana zaliczka. Użytkownik który dokonał rezerwacji ma obowiązek zapłacić pełną kwotę nie później niż 7 dni przed terminem rozpoczęcia wycieczki.

User może sobie wybrać rejon i na jego bazie będą wykonywane niektóre checki po stronie backendu

## Raporty
- Dla strony internetowej z aktualnymi ofertami


# Implementacja

## Funkcje systemu i uprawnienia
- [ ] Wystawianie potwierdeń płatności
- [ ] Tworzenie backupów
- [ ] Testowanie backupów
- [ ] Zmiana timetables
- [ ] Zarządzanie dokumentami
- [ ] Ubezpieczenia
- [ ] Tworzenie użytkowników
- [ ] Dodawanie atrakcji
- [ ] Zarządzanie istniejącymi tranzakcjami
- [ ] Anulowanie
- [ ] Opłata
- [ ] Potwierdzenie płatności
- [ ] Lista uczestników
- [ ] Opinie
- [ ] Zarządzanie organizacją wycieczki
- [ ] Tworzenie wycieczek
- [ ] Zarządzanie dostępnymi miejscami wycieczek
- [ ] Rezerwacja wycieczek
- [ ] Dodawanie listy uczestników wycieczki
- [ ] Generowanie raportów
- [ ] Edycja danych
- [ ] Tworzenie atrakcji dla danej wycieczki




<table>
    <tr>
        <th>Funkcja</th>
        <th>Osoba</th>
        <th>Opis</th>
    </tr>
    <tr>
        <td>Tworzenie konta</td>
        <td>Każdy użytkownik</td>
        <td>Aby dokonać rezerwacji każdy użytkownik musi założyć konto. Ma to na celu wygodną przyszłą edycję rezerwacji, której dokona oraz łatwy dostęp do wszelkich informacji na temat zarezerwowanych wycieczek. Konto może być założone przez osobę prywatną oraz firmy.</td>
    </tr>
        <tr>
        <td>Przeglądanie oferty</td>
        <td>Każdy użytkownik</td>
        <td>Każdy użytkownik może przeglądać ofertę wycieczek oraz atrakcje dostępne dla każdej wycieczki.</td>
    </tr>
    <tr>
        <td>Dokonywanie rezewacji</td>
        <td>Każdy zalogowany użytkownik</td>
        <td>Po zalogowaniu można dokonywać rezerwacji. Rezerwacji można dokononywać nie tylko dla siebie ale też dla innych. Przy rezerwacji należy podać liczbę uczesntików, ich dane oraz wybrane atrakcje.</td>
    </tr>
    <tr>
        <td>Anulowanie rezerwacji</td>
        <td>Osoba, która składała rezerwację</td>
        <td>Anulowania rezerwacji można dokonać tylko osoba, która składała rezerwację, a nie każdy jej uczestnik.</td>
    </tr>
    <tr>
        <td>Edycja wycieczki</td>
        <td>Osoba, która składała rezerwację</td>
        <td>Osoba, która składała rezerwację, może dodawać i usuwać uczestników wycieczki. Może również dodawć i usuwać atrakcję. Opcje te są dostępne tylko przed dokonaniem płatności.</td>
    </tr>
    <tr>
        <td>Dokonywanie płatności</td>
        <td>Osoba, która składała rezerwację</td>
        <td>Po dokonaniu rezerwacji użytkonik, który ją złożył nie później niż 7 dni przed terminem rozpoczęcia wycieczki musi dokonać płatności. </td>
    </tr>
    <tr>
        <td>Pobieranie odpowiednich dokumentów</td>
        <td>Osoba, która dokonała rezerwacji</td>
        <td>Osoba, która dokonała rezerwacji, może pobierać odpowiendie dokumenty, takie jak poetwierdzenie rezerwacji, potwierdzenie płatności, paragony, faktury, bilety na transport i/lub pobyt w hotelu</td>
    </tr>
    <tr>
        <td>Generowanie raportów</td>
        <td>Uprawniony pracownik biura</td>
        <td>Generowanie raportów dotyczące osób, które biorą udział w wycieczce, aby mogły zostać później przekazane np. dla kierowców, hoteli itp.</td>
    </tr>
    <tr>
        <td>Dodawnanie/usuwanie/edytowanie wycieczek</td>
        <td>Uprawniony pracownik biura</td>
        <td>Pracownik z odpowienimi uprawnieniami może dokonowyać zmian w wycieczkach (czas, miejsce noclegu, transport, koszt, dostępne dla danej wycieczki atrakcjie) oraz dodawć je i usuwać</td>
    </tr>
    <tr>
        <td>Sprawdzenie poprawności rezerwacji</td>
        <td>Uprawniony pracownik biura</td>
        <td>Przed upływem 7 dni przed rozpoczęciem wycieczki, odpowiedni pracownik może sprawdzić poprwaność złożonej rezerwacji, podanych danych itp. W przypadku złych danych i praku kontaktu z rezerwującym może anulować rezerwację</td>
    </tr>
    <tr>
        <td>Dodawanie nowego kraju, miasta, przewoźnika</td>
        <td>Uprawniony pracownik biura</td>
        <td>Pracownik biura ma możliwość dodania nowych destynacji, do jakich mogą odbywać się wycieczki. Może więc dodać do odpowiednich tabel rekordy. Może również dodać nowych przewoźników, jeśli takowi się znajdą.</td>
    </tr>
    <tr>
        <td>Tworzenie/testowanie backupów</td>
        <td>Administrator bazy danych</td>
        <td>Jako najbardziej uprawniony użytkonik systemu administrator może tworzyć, usuwać i testować backupy.</td>
    </tr>
    <tr>
        <td>Pełny dostęp do bazy danych</td>
        <td>Administrator bazy danych</td>
        <td>Administrator ma pełne uprawnienia do bazy danych. Może ją przeglądać i w razie potrzeby edytować</td>
    </tr>
</table>

# Schemat

<!--![projekt_diagram](https://hackmd.io/_uploads/r1g-1XY7A.png)-->
![Bazy_complete_4](https://hackmd.io/_uploads/Bk0YE83ER.svg)

# Szczególowy opis schematu

## Użytkownicy i pracownicy

### Accounts

![image](https://hackmd.io/_uploads/HynQnYHBA.png)

Aby rezerwować wycieczki trzeba być zajerestrowanym uczestnikiem. Dane konta (oprócz hasła), przechowywane są w tabeli account. Zawiera one dane, które posiada każde konto, takie jak: unikalny adres email, numer telefonu oraz unikalny username i autmatycznie generowane account ID (klucz główny). Oprócz tego zawiera również passwordID (klucz obcy). Jest to niezbędne, ponieważ hasła przechowywane są w odzielnej tabeli.

**accountID: klucz główny 
passwordID: klucz obcy łączący się z tabelą password 1:1.
email_addres: unikatowy adres email
username: unikatowa nazwa użytkowanika
surname: nazwisko**

```sql
CREATE TABLE [dbo].[Accounts] (
    [accountID]     INT          NOT NULL,
    [email_address] VARCHAR (60) NOT NULL,
    [username]      VARCHAR (40) NOT NULL,
    [phone]         VARCHAR (19) NOT NULL,
    [passwordID]    INT          NOT NULL,
    CONSTRAINT [PK_users] PRIMARY KEY CLUSTERED ([accountID] ASC),
    CONSTRAINT [Account_email_address 1] CHECK ([email_address] like '%[@]%[.]%'),
    CONSTRAINT [Account_phone] CHECK ([phone] like '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'),
    CONSTRAINT [Account_username] CHECK ([username] like '___%'),
    CONSTRAINT [PasswordID_FK] FOREIGN KEY ([passwordID]) REFERENCES [dbo].[Passwords] ([passwordID]),
    CONSTRAINT [Account_pk] UNIQUE NONCLUSTERED ([username] ASC),
    CONSTRAINT [Account_pk_2] UNIQUE NONCLUSTERED ([email_address] ASC),
    CONSTRAINT [Account_pk_3] UNIQUE NONCLUSTERED ([passwordID] ASC)
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>AccountID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>PasswordID</td>
        <td>FK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
        <tr>
        <td>username</td>
        <td></td>
        <td><code>[username] like '%[@]%[.]%'</code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>email_address</td>
        <td></td>
        <td><code>[email_address] like '%[@]%[.]%'</code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>phone</td>
        <td></td>
        <td><code>[phone] like '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'</code></td>
        <td></td>
    </tr>
</table>

### Company Clients

![image](https://hackmd.io/_uploads/B1X_2YSS0.png)

Tabela połączona w relacji 1 do 1 z tabelą account. Zawiera dane opisujące klienta będącego firmą. Zawiera pola takie jak NIP i nazwa firmy. Oczywiście posiada klucz główny i obcy jednocześnie account ID.

**accountID: klucz główny i obcy jednocześnie, łączy się z tabelą account
company_name: nazwa firmy
NIP: NIP**

```sql
CREATE TABLE [dbo].[CompanyClients] (
    [accountID]    INT           NOT NULL,
    [company_name] VARCHAR (150) NOT NULL,
    [NIP]          VARCHAR (10)  NOT NULL,
    CONSTRAINT [PK_CompanyClient] PRIMARY KEY CLUSTERED ([accountID] ASC),
    CONSTRAINT [CompanyClient_company_name] CHECK (NOT [company_name] like '%[^a-z._ ]%'),
    CONSTRAINT [CompanyClient_nip] CHECK ([NIP] like '__________'),
    CONSTRAINT [FK_CompanyClient_account] FOREIGN KEY ([accountID]) REFERENCES [dbo].[Accounts] ([accountID]),
    CONSTRAINT [CompanyClient_pk] UNIQUE NONCLUSTERED ([NIP] ASC),
    CONSTRAINT [CompanyClient_pk_2] UNIQUE NONCLUSTERED ([company_name] ASC)
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>accountID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>NIP</td>
        <td></td>
        <td><code>[NIP] like '__________'</code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>Company Name</td>
        <td></td>
        <td><code>NOT [company_name] like '%[^a-z]%'</code></td>
        <td>yes</td>
    </tr>
</table>

### Personal Clients

![image](https://hackmd.io/_uploads/H1xj2KBH0.png)

Tabela podobnie jak CompanyClient zawiera dane tym razem o kliencie będącym osobą prywatną. Zawiera pola identyfikujące daną osobę takie jak:
imię, nazwisko, adres, kod pocztowy oraz datę urodzenia. Zawiera również klucz obcy wskazujący na kraj, gdyż może się zdarzyć, że dana osoba mieszka za granicą. Klucz główny i obcy łączący z Account to accountID.
<b>
    - accountID: klucz główny i obcy łączący tabelę z tabelą Account 
    - cityID: klucz obcy wskazujący na tabelę City, wskazujące miejsce zamieszkania klienta
    - names: pole typu varchar wskazujące na imiona klienta które podaje się przy rejestracji konta
    - surname: pole typu varchar, zawiera nazwisko klienta, również potrzebne przy rejestracji
    - address: pole typu varchar, zawiera informację o numerze domu/mieszkania.
    - postal_code: pole typu varchar, wskazuje na kod pocztowy, potrzebne do ewentualnej korespondencji
    - birth_date: pole typu date, zawiera datę urodzenia klienta, wybrana zamiast numeru PESEL
</b>

```sql
CREATE TABLE [dbo].[PersonalClients] (
    [accountID]   INT           NOT NULL,
    [cityID]      INT           NOT NULL,
    [names]       VARCHAR (50)  NOT NULL,
    [surname]     VARCHAR (50)  NOT NULL,
    [address]     VARCHAR (200) NOT NULL,
    [postal_code] VARCHAR (6)   NOT NULL,
    [birth_date]  DATE          NOT NULL,
    CONSTRAINT [PK_PersonalClient] PRIMARY KEY CLUSTERED ([accountID] ASC),
    CONSTRAINT [PersonalClient_first_name] CHECK (NOT [names] like '%[^a-z]%'),
    CONSTRAINT [PersonalClient_last_name] CHECK (NOT [surname] like '%[^a-z]%'),
    CONSTRAINT [PersonalClient_postal_code] CHECK ([postal_code] like '__-___'),
    CONSTRAINT [FK_PersonalClient_account] FOREIGN KEY ([accountID]) REFERENCES [dbo].[Accounts] ([accountID]),
    CONSTRAINT [FK_PersonalClient_city] FOREIGN KEY ([cityID]) REFERENCES [dbo].[Cities] ([cityID])
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>accountID</td>
        <td>PK,FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>countryID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>names</td>
        <td></td>
        <td><code>NOT [names] like '%[^a-z]%'</code></td>
        <td></td>
    </tr>
    <tr>
        <td>last_name</td>
        <td></td>
        <td><code>NOT [surname] like '%[^a-z]%'</code></td>
        <td></td>
    </tr>
    <tr>
        <td>address</td>
        <td></td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>postal_code</td>
        <td></td>
        <td><code>[postal_code] like '__-___'</code></td>
        <td></td>
    </tr>
    <tr>
        <td>birth_date</td>
        <td></td>
        <td><code></code></td>
        <td></td>
    </tr>
</table>

### Employees

![image](https://hackmd.io/_uploads/rkxy6tSr0.png)

Na bardzo podobnej zasadzie jak tabela account, ale dla pracowników nie tworzymy usernamów. Tabela employee przechowuje również ID działu w którym dana osoba pracuje.

**employeeID: klucz główny
passwordID: klucz obcy, łączy się z tabelą password
departmentID: klucz obcy, łączy się ze słownikiem department
names: imiona
surname: nazwisko
email_address: unikatowy adres email
phone: numer telefonu**

```sql
CREATE TABLE [dbo].[Employees] (
    [employeeID]   INT           NOT NULL,
    [passwordID]   INT           NOT NULL,
    [departmentID] INT           NOT NULL,
    [names]        VARCHAR (60)  NOT NULL,
    [surname]      VARCHAR (100) NOT NULL,
    [email]        VARCHAR (50)  NOT NULL,
    [phone]        VARCHAR (19)  NOT NULL,
    CONSTRAINT [employee_pk] PRIMARY KEY CLUSTERED ([employeeID] ASC),
    CONSTRAINT [Employee_email] CHECK ([email] like '%[@]%[.]%'),
    CONSTRAINT [Employee_name] CHECK (NOT [names] like '%[^a-z]%'),
    CONSTRAINT [Employee_phone] CHECK ([phone] like '_________'),
    CONSTRAINT [Employee_surname] CHECK (NOT [surname] like '%[^a-z]%'),
    CONSTRAINT [DepartamentID___fk] FOREIGN KEY ([departmentID]) REFERENCES [dbo].[Departments] ([departmentID]),
    CONSTRAINT [employeeID] FOREIGN KEY ([passwordID]) REFERENCES [dbo].[Passwords] ([passwordID]),
    CONSTRAINT [Employee_pk_1] UNIQUE NONCLUSTERED ([passwordID] ASC),
    CONSTRAINT [Employee_pk_2] UNIQUE NONCLUSTERED ([email] ASC),
    CONSTRAINT [Employee_pk_3] UNIQUE NONCLUSTERED ([phone] ASC)
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>employeeID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>passwordID</td>
        <td>FK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>departmentID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>names</td>
        <td></td>
        <td><code>NOT [names] like '%[^a-z]%'</code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>surname</td>
        <td></td>
        <td><code>NOT [surname] like '%[^a-z]%'</code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>email</td>
        <td></td>
        <td><code>[email] like '%[@]%[.]%'</code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>phone</td>
        <td></td>
        <td><code>[phone] like '_________'</code></td>
        <td>yes</td>
    </tr>
</table>

### Departaments

![image](https://hackmd.io/_uploads/rkeNaYrHC.png)

Słownik przechowujący nazwy działów i odpowiadające im ID (klucz główny).

**departmentID: klucz główny
department_name: nazwa działu**

```sql
CREATE TABLE [dbo].[Departments] (
    [departmentID]    INT          NOT NULL,
    [department_name] VARCHAR (50) NOT NULL,
    CONSTRAINT [DepartamentID_pk] PRIMARY KEY CLUSTERED ([departmentID] ASC),
    CONSTRAINT [Department_department_name] CHECK (NOT [department_name] like '%[^a-z ]%')
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>departamentID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>departament_name</td>
        <td></td>
        <td><code>NOT [departament_name] like '%[^a-z ]%'</code></td>
        <td></td>
    </tr>
</table>

### Passwords

![image](https://hackmd.io/_uploads/SJ-U6tSBR.png)

W tabeli password przechowywane są hasła w postaci hashy, używaną sól oraz klucz główny password ID. Po tym kluczu tabela password łączy się z tabelą account oraz employee 1:1.

**passwordID: klucz główny
hash: haseł nie przechowujemy w formie jawnej tylko w formie hasha
salt: sól używana do hasha (zakładamy, że zawsze dodajemy ją po pierwszym znaku hasła)**

```sql
CREATE TABLE [dbo].[Passwords] (
    [passwordID] INT           NOT NULL,
    [hash]       VARCHAR (100) NOT NULL,
    [salt]       VARCHAR (100) NOT NULL,
    CONSTRAINT [passwordID] PRIMARY KEY CLUSTERED ([passwordID] ASC),
    CONSTRAINT [Password_pk] UNIQUE NONCLUSTERED ([hash] ASC),
    CONSTRAINT [Password_pk_2] UNIQUE NONCLUSTERED ([salt] ASC)
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>passwordID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>hash</td>
        <td></td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>salt</td>
        <td></td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
</table>

## Wycieczki i rezerwacje

### Countries

![image](https://hackmd.io/_uploads/HJxzzJq7A.png)

Słownik przechowujący nazwy krajów i odpowiadające im ID (klucz główny)

**countryID: klucz główny
country_name: nazwa kraju**

```sql
CREATE TABLE [dbo].[Countries] (
    [countryID]    INT           NOT NULL,
    [country_name] VARCHAR (100) NOT NULL,
    CONSTRAINT [PK_Country] PRIMARY KEY CLUSTERED ([countryID] ASC),
    CONSTRAINT [Country_country_name] CHECK (NOT [country_name] like '%[^a-z ]%'),
    CONSTRAINT [Country_pk] UNIQUE NONCLUSTERED ([country_name] ASC)
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>countryID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>country_Name</td>
        <td></td>
        <td><code>NOT [country_name] like '%[^a-z ]%'</code></td>
        <td>yes</td>
    </tr>
</table>

### Cities

![image](https://hackmd.io/_uploads/HJvAxy5XC.png)

Słownik przechowujący nazwy miast i odpowiadające im ID (klucz główny). Tabela przechowuje również państwa, w których dane miasta się znajdują
    
**cityID: klucz główny
countryID: klucz obcy łączący się z tabelą countries
city_name: nazwa miasta**

```sql
CREATE TABLE [dbo].[Cities] (
    [cityID]    INT           NOT NULL,
    [city_name] VARCHAR (100) NOT NULL,
    [countryID] INT           NOT NULL,
    CONSTRAINT [cityID] PRIMARY KEY CLUSTERED ([cityID] ASC),
    CONSTRAINT [City_city_name ] CHECK (NOT [city_name] like '%[^a-z -]%'),
    CONSTRAINT [countryID] FOREIGN KEY ([countryID]) REFERENCES [dbo].[Countries] ([countryID]),
    CONSTRAINT [City_pk] UNIQUE NONCLUSTERED ([city_name] ASC)
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>cityID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>city_name</td>
        <td></td>
        <td><code>NOT [city_name] like '%[^a-z -]%'</code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>countryID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
</table>

### Offers

![image](https://hackmd.io/_uploads/S1_M-15mR.png)


Tabela offer przechowuje ID miasta z którego zaczyna i kończy się wycieczka, aby nie powielać tych informacji w tabeli Tour.

<b>

- offerID: klucz główny, pole typu int, automatycznie inkrementowane podczas dodawaniu rekordów, jednoznacznie definiuje dany rekord.    
- starting_city: pole typu int, klucz obcy do tabeli Cities, wskazuje na miejsce z którego będzie zaczynać się wycieczka
- destination_city: pole typu int, klucz obcy do tabeli Cities, wskazuje na miejsce docelowe wycieczki
</b>

```sql
CREATE TABLE [dbo].[Offers] (
    [offerID]            INT NOT NULL,
    [starting_cityID]    INT NOT NULL,
    [destination_cityID] INT NOT NULL,
    CONSTRAINT [offerID] PRIMARY KEY CLUSTERED ([offerID] ASC),
    CONSTRAINT [end_cityID_fk] FOREIGN KEY ([destination_cityID]) REFERENCES [dbo].[Cities] ([cityID]),
    CONSTRAINT [start_cityID_fk] FOREIGN KEY ([starting_cityID]) REFERENCES [dbo].[Cities] ([cityID])
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>offerID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>starting_cityID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>destination_cityID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
</table>

### Tours

![image](https://hackmd.io/_uploads/SJQ1RFBB0.png)

Tour jest to tabela zawierająca szczególowy opis wycieczki. Zawiera informacje o dacie rozpoczęcia i końca, limicie uczestików, cenie oraz pracowniku, który jest "patronem" wycieczki. Tabela zawiera również klucz obcy offerID. Celem takiego rozwiącania jest to, że dla jednej offer możne być wiele Tour w różnych datach (czy nawet z innymi przewoźnikami, innym patronem wycieczki czy innym limitem osób). Kluczem główny to atomatycznie generowany tourID

**tourID: klucz główny
offerID: klucz obcy łączący się z tabelą offer
carrierID: klucz obcy łączący się z tabelą carrier
employee_supervisor: klucz obcy łączący się z tabelą employee
start_date: data początku
end_date: data końca
max_participants: maksymalna liczba uczestników

```sql
CREATE TABLE [dbo].[Tours] (
    [tourID]             INT      NOT NULL,
    [offerID]            INT      NOT NULL,
    [start_date]         DATETIME NOT NULL,
    [end_date]           DATETIME NOT NULL,
    [participants_limit] INT      NOT NULL,
    [carrierID]          INT      NOT NULL,
    [price]              MONEY    NOT NULL,
    [tour_supervisor]    INT      NOT NULL,
    CONSTRAINT [TourID] PRIMARY KEY CLUSTERED ([tourID] ASC),
    CONSTRAINT [Tour_participants_limit] CHECK (NOT [participants_limit] like '%[^0-9]%'),
    CONSTRAINT [Tour_price] CHECK (NOT [price] like '%[^0-9.]%'),
    CONSTRAINT [FK_Tour_employee] FOREIGN KEY ([tour_supervisor]) REFERENCES [dbo].[Employees] ([employeeID]),
    CONSTRAINT [Tour_Offer_offerID_fk] FOREIGN KEY ([offerID]) REFERENCES [dbo].[Offers] ([offerID])
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>tourID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>offerID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>carrierID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>tour_supervisor</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>price</td>
        <td></td>
        <td><code>NOT [price] like '%[^0-9.]%'</code></td>
        <td></td>
    </tr>
    <tr>
        <td>participants_limit</td>
        <td></td>
        <td><code>NOT [participants_limit] like '%[^0-9]%'</code></td>
        <td></td>
    </tr>
    <tr>
        <td>start_date</td>
        <td></td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>end_date</td>
        <td></td>
        <td><code></code></td>
        <td></td>
    </tr>
</table>

### Carriers

![image](https://hackmd.io/_uploads/SyuAWJcm0.png)

Słownik, który przechowuje nazwę przewoźnika i odpowiadające mu ID (klucz główny)

**carrierID: klucz główny
carrier_name: nazwa przewoźnika**

```sql
CREATE TABLE [dbo].[Carriers] (
    [carrierID]    INT           NOT NULL,
    [carrier_name] VARCHAR (100) NOT NULL,
    CONSTRAINT [carrierID_pk] PRIMARY KEY CLUSTERED ([carrierID] ASC),
    CONSTRAINT [carriers_name ] CHECK ([carrier_name] like '%[a-zA-Z_- . ]%'),
    CONSTRAINT [Carriers_uk] UNIQUE NONCLUSTERED ([carrier_name] ASC)
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>carrierID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>carrier_name</td>
        <td></td>
        <td><code>[carrier_name] like '%[a-zA-Z_- . ]%'</code></td>
        <td>yes</td>
    </tr>
</table>

### Reservations

![image](https://hackmd.io/_uploads/HJFEfJ9QC.png)

Rezerwacji jast przypisana do użytkownika z kontem, który ją składał poprzez klucz obcy accountID. Użytkownik rezerwuje tour a nie offer (kolejny klucz obcy - tourID). Ważne jest to, że użytkownik może złożyć rezerwację nie tylko dla siebie (wszystkie osoby są umieszczane w tabeli participants). Stąd też kolumna summary_price oraz reserved_seats. Oczywiście tabela zawiera też automatycznie generowany klucz główny reservationID.

```sql
CREATE TABLE [dbo].[Reservations] (
    [reservationID]  INT NOT NULL,
    [accountID]      INT NOT NULL,
    [tourID]         INT NOT NULL,
    [reserved_seats] INT NOT NULL,
    CONSTRAINT [reservationID_pk] PRIMARY KEY CLUSTERED ([reservationID] ASC),
    CONSTRAINT [Reservation_reserved_seats] CHECK (NOT [reserved_seats] like '%[^0-9]%'),
    CONSTRAINT [accountID_fk] FOREIGN KEY ([accountID]) REFERENCES [dbo].[Accounts] ([accountID]),
    CONSTRAINT [tourID___fk] FOREIGN KEY ([tourID]) REFERENCES [dbo].[Tours] ([tourID])
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>reservationID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>accountID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>tourID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>reserved_seats</td>
        <td></td>
        <td><code>NOT [reserved_seats] like '%[^0-9]%'</code></td>
        <td></td>
    </tr>
</table>

### Participants

![image](https://hackmd.io/_uploads/HJd70KSS0.png)

Dokładne dane uczestników zgłoszonych przy rezerwacji przechowywane są w tabeli participants (tabela participants łączy się z tabelą reservation po kluczy obcym reservation ID w sposób wiele do 1). Tabela przechowuje dane użytkowników tj. imię, nazwisko oraz data urodzenia (zamiast peselu ze względu na RODO). Oczywiście tabela zawiera też automatycznie generowany klucz główny participantsID.

<b>
- participantID: pole typu int, klucz główny, autoinkrementowany. Jednoznacznie wskazuje na danego uczestnika
- reservationID: pole typu int, klucz obcy, wskazuje na rekord w tabeli reservation, pokazuje z jakiej rezerwacji pochodzi dany uczestnik.
- names: pole typu varchar, zawiera imiona uczestnika, służące do zidentyfikowania osoby
- surname: pole typu varchar, zawiera nazwisko uczestnika, służące do zidentyfikowania osoby
</b>

```sql
CREATE TABLE [dbo].[Participants] (
    [participantID] INT          NOT NULL,
    [names]         VARCHAR (30) NOT NULL,
    [surname]       VARCHAR (30) NOT NULL,
    [birth_date]    DATE         NOT NULL,
    [reservationID] INT          NOT NULL,
    CONSTRAINT [PK_Participant] PRIMARY KEY CLUSTERED ([participantID] ASC),
    CONSTRAINT [Participant_first_name] CHECK (NOT [names] like '%[^a-z]%'),
    CONSTRAINT [Participant_last_name] CHECK (NOT [surname] like '%[^a-z]%'),
    CONSTRAINT [reservationID_fk] FOREIGN KEY ([reservationID]) REFERENCES [dbo].[Reservations] ([reservationID])
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>participantID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>reservationID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>names</td>
        <td></td>
        <td><code>NOT [names] like '%[^a-z]%'</code></td>
        <td></td>
    </tr>
    <tr>
        <td>surname</td>
        <td></td>
        <td><code>NOT [surname] like '%[^a-z]%'</code></td>
        <td></td>
    </tr>
    <tr>
        <td>birth_date</td>
        <td></td>
        <td><code></code></td>
        <td></td>
    </tr>
</table>

## Atrakcje

### Attractions

![image](https://hackmd.io/_uploads/r1PW7J9XA.png)

Nazwy atrakcji i klucze im przypisane przechowywane są w słowniku Attractions

<b>
- attractionID: klucz główny, autoinkrementowane pole typu int, identyfikuje daną atrakcję
- attraction_name: pole typu varchar, zawiera nazwę atrakcji
</b>

```sql
CREATE TABLE [dbo].[Attractions] (
    [attractionID]    INT           NOT NULL,
    [attraction_name] VARCHAR (100) NOT NULL,
    CONSTRAINT [PK_Attractions] PRIMARY KEY CLUSTERED ([attractionID] ASC),
    CONSTRAINT [Attraction_attraction] CHECK (NOT [attraction_name] like '%[^a-zA-Z ]%'),
    CONSTRAINT [Attraction_pk] UNIQUE NONCLUSTERED ([attraction_name] ASC)
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>attractionID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>attraction_name</td>
        <td></td>
        <td><code>NOT [attraction_name] like '%[^a-zA-Z ]%'</code></td>
        <td>yes</td>
    </tr>
</table>

### Tour Attractions

![image](https://hackmd.io/_uploads/HJsDAtSHC.png)

Więcej informacji o atrakcjach czyli do jakiego touru są przypisane (poprzez klucz obcy-tourID), ile maksymalnie uczestników może wziąć udział w danej atrakcji oraz ile kosztuje przechowywane są w tabeli Tour attractions.

**tour_attractionID: klucz główny
tourID: klucz obcy łączący się z tabelą tour
attractionID: klucz obcy łączący się ze słownikiem attraction
max_participants: maksymalna liczba uczestników
base_price: cena bazowa
price_per_partcipants: cena za osobę**

```sql
CREATE TABLE [dbo].[TourAttractions] (
    [tour_attractionID]     INT   NOT NULL,
    [tourID]                INT   NOT NULL,
    [attractionID]          INT   NOT NULL,
    [max_participants]      INT   NOT NULL,
    [price_per_participant] MONEY NOT NULL,
    CONSTRAINT [PK_Tour Attactions] PRIMARY KEY CLUSTERED ([tour_attractionID] ASC),
    CONSTRAINT [TourAttractions_max_participants] CHECK (NOT [max_participants] like '%[^0-9]%'),
    CONSTRAINT [TourAttractions_price_per_participant ] CHECK (NOT [price_per_participant] like '%[^0-9.]%'),
    CONSTRAINT [attractionID_fk] FOREIGN KEY ([attractionID]) REFERENCES [dbo].[Attractions] ([attractionID]),
    CONSTRAINT [TourID_fk] FOREIGN KEY ([tourID]) REFERENCES [dbo].[Tours] ([tourID])
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>tour_attractionID</td>
        <td>PK</td>
        <td><code></code></td>
        <td>yes</td>
    </tr>
    <tr>
        <td>attractionID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>tourID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>max_participants</td>
        <td></td>
        <td><code>[max_participants] like '%[^0-9]%'</code></td>
        <td></td>
    </tr>
    <tr>
        <td>price_per_participant</td>
        <td></td>
        <td><code>[price_per_participant] like '%[^0-9.]%'</code></td>
        <td></td>
    </tr>
</table>

### Reserved Attractions

![image](https://hackmd.io/_uploads/BJdIQJ5QC.png)

Informacje o tym kto zarezerwował jaką atrakcję przechowywane są w tabeli Reserved Attractions. Tabela zawiera dwa klucze obce: tour_attractionID oraz participantID.

**tour_attractoinID: klucz główny i obcy łączący się z tabelą Tour Attractions
participantID:klucz główny i obcy łączący się z tabelą participants**

```sql
CREATE TABLE [dbo].[ReservedAttractions] (
    [tour_attractionID] INT NOT NULL,
    [participantID]     INT NOT NULL,
    CONSTRAINT [PK_ReservedAttraction] PRIMARY KEY CLUSTERED ([tour_attractionID] ASC, [participantID] ASC),
    CONSTRAINT [participantID_fk] FOREIGN KEY ([participantID]) REFERENCES [dbo].[Participants] ([participantID]),
    CONSTRAINT [tour_attractionID_fk] FOREIGN KEY ([tour_attractionID]) REFERENCES [dbo].[TourAttractions] ([tour_attractionID])
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>participantID</td>
        <td>PK/FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>tour_attractionID</td>
        <td>PK/FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
</table>

### Payments

![image](https://hackmd.io/_uploads/BkwC0KBSA.png)

Tabela przechowuje informację o płatności za rezerwację oraz datę jej uiszczenia.

**paymentID: klucz główny do indeksowania płatności
reservationID: klucz obcy określający jakiej rezerwacji dotyczy płatność
amount_paid: wartość typu money określający kwotę wpłaty
payment_date: data płatności typu date**

```sql
CREATE TABLE [dbo].[Payments] (
    [paymentID]     INT   NOT NULL,
    [reservationID] INT   NOT NULL,
    [amount_paid]   MONEY NOT NULL,
    [payment_date]  DATE  NOT NULL,
    CONSTRAINT [PK_Payments] PRIMARY KEY CLUSTERED ([paymentID] ASC),
    CONSTRAINT [Payments_amount_paid] CHECK ([amount_paid]>(0)),
    CONSTRAINT [FK_Payments_Reservation] FOREIGN KEY ([reservationID]) REFERENCES [dbo].[Reservations] ([reservationID])
);
```

#### Warunki integralności

<table>
    <tr>
        <th>Pole</th>
        <th>Key</th>
        <th>Check</th>
        <th>Unique</th>
    </tr>
    <tr>
        <td>paymentID</td>
        <td>PK</td>
        <td><code></code></td>
        <td></td>
    </tr>
    <tr>
        <td>reservationID</td>
        <td>FK</td>
        <td><code></code></td>
        <td></td>
    </tr>
        <tr>
        <td>amount_paid</td>
        <td></td>
        <td><code>[amount_paid]>(0)</code></td>
        <td></td>
    </tr>
        <tr>
        <td>payment_date</td>
        <td></td>
        <td><code></code></td>
        <td></td>
    </tr>
</table>

# Widoki

## Login backend - Dominik Motyl

```sql=
SELECT hash, salt, lower(concat(names,'.',surname)) AS username, email AS email, phone FROM passwords p
JOIN Employees e ON p.passwordID = e.passwordID
UNION
SELECT hash, salt, username, email_address AS email, phone FROM passwords p
JOIN Accounts a ON p.passwordID = a.passwordID
```

## Reservation view - Dominik Motyl

```sql=
SELECT username, email_address AS email, phone, names, surname, address, postal_code, birth_date, company_name, NIP, r.tourID, r.reserved_seats FROM Accounts a
LEFT JOIN dbo.PersonalClients pc ON a.accountID = pc.accountID
LEFT JOIN dbo.CompanyClients cc ON a.accountID = cc.accountID
LEFT JOIN Reservations r ON a.accountID = r.accountID
```

## Tour view - Dominik Motyl

```sql=
SELECT t.tourID, email, start_date, end_date, participants_limit, max_participants AS [max_participants_per_attraction], reserved_seats AS [reserved_tour_seats], price, price_per_participant, attraction_name, carrier_name FROM Tours t
JOIN TourAttractions ta ON t.tourID = ta.tourID
JOIN Attractions a ON ta.attractionID = a.attractionID
JOIN Reservations r ON t.tourID = r.tourID
JOIN ReservedAttractions ra ON ta.tour_attractionID = ra.tour_attractionID
JOIN Carriers c ON t.carrierID = c.carrierID
JOIN Employees e ON t.tour_supervisor = e.employeeID
```

## Website view - Dominik Motyl

```sql=
SELECT cs.city_name AS [Start],
           ce.city_name AS [End],
           t.participants_limit,
           t.price,
           ca.carrier_name,
           em.email
    FROM Tours t
             JOIN Offers o ON t.offerID = o.offerID
             JOIN Cities cs ON o.starting_cityID = cs.cityID
             JOIN Cities ce ON o.destination_cityID = ce.cityID
             JOIN Carriers ca ON t.carrierID = ca.carrierID
             JOIN Employees em ON t.tour_supervisor = em.employeeID
```

## Reservation payment details - Michał Zięba

```sql=
SELECT r.reservationID,
           (isnull(cena_atrakcji, 0))                                    as cena_atrakcji,
           price                                                         as cena_wycieczki,
           (isnull(cena_atrakcji, 0) + price)                            as cena_laczna,
           isnull(P2.amount_paid, 0)                                     as kwota_zaplacona,
           (isnull(amount_paid, 0) - (isnull(cena_atrakcji, 0) + price)) as 'nadplata/niedoplata'
    from Reservations r
             left join
         (Select p.reservationID, SUM(price_per_participant) as cena_atrakcji
          from Participants p
                   join dbo.ReservedAttractions RA on p.participantID = RA.participantID
                   join dbo.TourAttractions TA on RA.tour_attractionID = TA.tour_attractionID
          group by p.reservationID)
             as pom2 on r.reservationID = pom2.reservationID
             join
         (select r.reservationID, (reserved_seats * price) as price
          from Reservations r
                   join dbo.Tours t on r.tourID = t.tourID
          group by r.reservationID, (reserved_seats * price)) as pom on r.reservationID = pom.reservationID
             left join dbo.Payments P2 on r.reservationID = P2.reservationID
    group by r.reservationID, cena_atrakcji, price, (cena_atrakcji + price), amount_paid
```

## Company Clients Reservation - Mikołaj Kraczek

```sql=
select P.reservationID as 'participantID', P. names as 'participant_name', 
P.surname as 'participant_surname', R.accountID as 'bookerID',
CC.company_name as 'booker_name', CC.NIP as 'booker_NIP', T.tourID
from CompanyClients CC join dbo.Reservations R on CC.accountID = R.accountID
join Participants P on R.reservationID = P.reservationID
join Tours T on R.tourID = T.tourID
```

## Personal Clients Reservation - Mikołaj Kraczek

```sql=
select P.reservationID as 'participantID', P. names as 'participant_name', 
P.surname as 'participant_surname', R.accountID as 'bookerID', 
PC.names as 'booker_name', PC.surname as 'booker_surname', T.tourID
from PersonalClients PC join dbo.Reservations R on PC.accountID = R.accountID
join Participants P on R.reservationID = P.reservationID
join Tours T on R.tourID = T.tourID
```

## Wyświetlanie danych uczestnika oraz jego atrakcji - Tomasz Leniart

```sql=
CREATE VIEW [dbo].[ParticipantsAttractionsInReservations] AS
SELECT 
    r.tourID as Tour,
    r.reservationID as Reservation,
    p.participantID as Participant,
    p.names as Names,
    p.surname as Surname,
    p.birth_date as [Birth date],
    ta.tour_attractionID
FROM 
    dbo.Tours t
    JOIN dbo.Reservations r ON t.tourID = r.tourID
    JOIN dbo.Participants p ON r.reservationID = p.reservationID
    JOIN dbo.ReservedAttractions ra ON p.participantID = ra.participantID
    JOIN dbo.TourAttractions ta ON ra.tour_attractionID = ta.tour_attractionID
```

# Funkcje

## CountParticipant - Mikołaj Kraczek

```sql=
CREATE FUNCTION CountParticipants(
    @FirstName VARCHAR(30),
    @LastName VARCHAR(30),
    @BirthDate DATE
)
RETURNS INT
AS
BEGIN
    DECLARE @Count INT;

    SELECT @Count = COUNT(*)
    FROM Participants
    WHERE names = @FirstName
    AND surname = @LastName
    AND birth_date = @BirthDate;

    RETURN @Count;
END;
```

## Dodaje do daty 2 tygodnie - Dominik Motyl

```sql
CREATE FUNCTION twoWeeksLater (@d DATETIME)
RETURNS DATETIME
AS
BEGIN
    DECLARE @result DATETIME;
    SET @result = DATEADD(WEEK, 2, @d);
    RETURN @result;
END;
```

## Wyswietlenie ilosci rezerwacji po ID konta - Michał Zięba

```sql=
CREATE FUNCTION [dbo].[GetReservationCountByAccount] (@AccountID int)
RETURNS int
AS
BEGIN
    DECLARE @ReservationCount int;
    SELECT @ReservationCount = COUNT(*)
    FROM Reservations
    WHERE accountID = @AccountID;

    RETURN @ReservationCount;
END
```

## Wyswietlenie sumy wplat po ID konta - Michał Zięba

```sql=
CREATE FUNCTION [dbo].[GetTotalAmountPaidByAccount] (@AccountID int)
RETURNS money
AS
BEGIN
    DECLARE @TotalAmountPaid money;

    SELECT @TotalAmountPaid = SUM(ISNULL(p.amount_paid, 0))
    FROM Reservations r
    LEFT JOIN Payments p ON r.reservationID = p.reservationID
    WHERE r.accountID = @AccountID;

    RETURN @TotalAmountPaid;
END
```

## Dla podanego NIP i ReservationID wyświetla ilość członków - Tomasz Leniart

```sql=
CREATE FUNCTION dbo.GetParticipantCountByNIPAndReservation (
    @NIP VARCHAR(10),
    @reservationID INT
)
RETURNS INT
AS
BEGIN
    DECLARE @accountID INT;
    DECLARE @participantCount INT;

    SELECT @accountID = accountID
    FROM dbo.CompanyClients
    WHERE NIP = @NIP;

    IF EXISTS (
        SELECT 1
        FROM dbo.Reservations
        WHERE reservationID = @reservationID
        AND accountID = @accountID
    )
    BEGIN
        SELECT @participantCount = COUNT(*)
        FROM dbo.Participants
        WHERE reservationID = @reservationID;
    END
    ELSE
    BEGIN
        SET @participantCount = 0;
    END

    RETURN @participantCount;
END;
```

# Procedury
## Dodawanie rezerwacji - Mikołaj Kraczek
```sql=
CREATE PROCEDURE AddReservation
    @reservationID INT,
    @accountID INT,
    @tourID INT,
    @reservedSeats INT
AS
BEGIN
    -- Wstawienie nowego rekordu do tabeli Reservations
    INSERT INTO Reservations (reservationID, accountID, tourID, reserved_seats)
    VALUES (@reservationID, @accountID, @tourID, @reservedSeats);

    PRINT 'Reservation added successfully';
END;
```

## Dodawanie participanta - Mikołaj Kraczek
```sql=
CREATE PROCEDURE AddParticipant
    @participantID INT,
    @reservationID INT,
    @name VARCHAR(100),
    @surname VARCHAR(100),
    @birth_date DATE
AS
BEGIN
    -- Wstawienie nowego rekordu do tabeli Participants
    INSERT INTO Participants (participantID, reservationID, names, surname, birth_date)
    VALUES (@participantID, @reservationID, @name, @surname, @birth_date);

    PRINT 'Participant added successfully';
END;
```

## Dodawanie rekordu w tabeli Passwords - Dominik Motyl
```sql=
CREATE PROCEDURE dbo.AddPasswordRecord
    @passwordID INT,
    @hash VARCHAR(100),
    @salt VARCHAR(100)
AS
BEGIN
    INSERT INTO dbo.Passwords (passwordID, hash, salt)
    VALUES (@passwordID, @hash, @salt);
END;
GO
```
## Dodawanie rekordu do tabeli Reserved Attraction - Michał Zięba
```sql=
CREATE PROCEDURE AddReservedAttraction
    @tour_attractionID INT,
    @participantID INT
AS
BEGIN
    -- Wstawienie nowego rekordu do tabeli reserved_attractions
    INSERT INTO reserved_attractions (tour_attraction_ID, participant_ID)
    VALUES (@tourAttractionID, @participantID);

    PRINT 'Reserved attraction added successfully';
END;
```

## Wyświetlanie tours dla przedziału cenowego - Mikołaj Kraczek

```sql=
CREATE PROCEDURE GetToursByPriceRange
    @MinPrice MONEY,
    @MaxPrice MONEY
AS
BEGIN
    SELECT tourID, offerID, start_date, end_date, price
    FROM Tours
    WHERE price BETWEEN @MinPrice AND @MaxPrice;
END;
```

## Wypisuje Atrakcje dla danej rezerwacji - Dominik Motyl

```sql=
ALTER PROCEDURE printAttraction @reservationID int
AS
SELECT a.attraction_name, count(ra.participantID) as participantsNumber, sum(price_per_participant) as summaryPrice
FROM Participants p
JOIN ReservedAttractions ra ON p.participantID = ra.participantID
JOIN TourAttractions ta ON ra.tour_attractionID = ta.tour_attractionID
JOIN Attractions a ON ta.attractionID = a.attractionID
WHERE p.reservationID = @reservationID GROUP BY attraction_name
GO;

EXEC printAttraction @reservationID = 2
```

## Wyswietlenie uczestnikow urodzonych w przedziale dat - Michał Zięba

```sql=
CREATE PROCEDURE [dbo].[GetParticipantsByBirthDate]
    @StartDate DATE,
    @EndDate DATE
AS
BEGIN
    SELECT
        participantID,
        names,
        surname,
        birth_date,
        reservationID
    FROM
        Participants
    WHERE
        birth_date BETWEEN @StartDate AND @EndDate
    ORDER BY
        birth_date;
END;
```

## Wyświetlanie wycieczek, które dopiero się odbędą dla danego przewodnika - Tomasz Leniart

```sql=
USE [u_mizieba];
GO

CREATE PROCEDURE GetUpcomingToursByCarrier
    @CarrierName VARCHAR(100)
AS
BEGIN
    -- Get the current date and time
    DECLARE @CurrentDateTime DATETIME;
    SET @CurrentDateTime = GETDATE();

    SELECT
        t.tourID,
        t.offerID,
        t.start_date,
        t.end_date,
        t.participants_limit,
        t.price,
        t.tour_supervisor
    FROM
        dbo.Tours t
    INNER JOIN
        dbo.Carriers c ON t.carrierID = c.carrierID
    WHERE
        c.carrier_name = @CarrierName
        AND t.start_date > @CurrentDateTime;
END;
GO
```

## Dodawanie rekordu do tabel Accounts i PersonalClient/CompanyClient - Tomasz Leniart

```sql
GO
CREATE PROCEDURE AddClientRecord
    @accountID INT,
    @Email VARCHAR(60),
    @Username VARCHAR(40),
    @Phone VARCHAR(19),
    @PasswordID INT,
    @ClientType CHAR(1), -- 'P' for PersonalClient, 'C' for CompanyClient
    @CityID INT = NULL,
    @Names VARCHAR(50) = NULL,
    @Surname VARCHAR(50) = NULL,
    @Address VARCHAR(200) = NULL,
    @PostalCode VARCHAR(6) = NULL,
    @BirthDate DATE = NULL,
    @CompanyName VARCHAR(150) = NULL,
    @NIP VARCHAR(10) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRY
        -- Start transaction
        BEGIN TRANSACTION;

        -- Insert into Accounts table
        INSERT INTO dbo.Accounts (accountID, email_address, username, phone, passwordID)
        VALUES (@accountID, @Email, @Username, @Phone, @PasswordID);

        -- Insert into PersonalClients or CompanyClients based on ClientType
        IF @ClientType = 'P'
        BEGIN
            -- Insert into PersonalClients table
            INSERT INTO dbo.PersonalClients (accountID, cityID, names, surname, address, postal_code, birth_date)
            VALUES (@accountID, @CityID, @Names, @Surname, @Address, @PostalCode, @BirthDate);
        END
        ELSE IF @ClientType = 'C'
        BEGIN
            -- Insert into CompanyClients table
            INSERT INTO dbo.CompanyClients (accountID, company_name, NIP)
            VALUES (@accountID, @CompanyName, @NIP);
        END
        ELSE
        BEGIN
            -- Raise an error if ClientType is not valid
            THROW  5, 'Invalid ClientType. Use "P" for PersonalClient or "C" for CompanyClient.', 1;
        END

        -- Commit transaction
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        -- Rollback transaction if any error occurs
        ROLLBACK TRANSACTION;

        -- Raise the error
        THROW;
    END CATCH;
END
GO
```

# Triggery

## Próbujemy zapodiegać duplikowniu się haseł - Dominik Motyl

```sql=
CREATE TRIGGER trg_PreventDuplicatePassword
ON dbo.Passwords
INSTEAD OF INSERT
AS
BEGIN
    IF EXISTS (SELECT 1 FROM dbo.Passwords p
               INNER JOIN inserted i ON p.hash = i.hash)
    BEGIN
        RAISERROR('Duplicate hash value is not allowed.', 16, 1)
        ROLLBACK TRANSACTION
        RETURN
    END
    IF EXISTS (SELECT 1 FROM dbo.Passwords p
               INNER JOIN inserted i ON p.salt = i.salt)
    BEGIN
        RAISERROR('Duplicate salt value is not allowed.', 16, 1)
        ROLLBACK TRANSACTION
        RETURN
    END
    INSERT INTO dbo.Passwords (passwordID, hash, salt)
    SELECT passwordID, hash, salt
    FROM inserted
END
```

## Sprawdzanie ilości wolnych miejsc - Mikołaj Kraczek

```sql=
CREATE TRIGGER before_insert_reservations ON Reservations
INSTEAD OF INSERT
AS
BEGIN
    DECLARE @tourID INT;
    DECLARE @reservedSeats INT;
    DECLARE @totalReserved INT;
    DECLARE @maxSeats INT;

    SELECT @tourID = tourID, @reservedSeats = reserved_seats
    FROM INSERTED;
    SELECT @totalReserved = ISNULL(SUM(reserved_seats), 0)
    FROM Reservations
    WHERE tourID = @tourID;
    SELECT @maxSeats = participants_limit
    FROM Tours
    WHERE tourID = @tourID;
    IF @totalReserved + @reservedSeats > @maxSeats
    BEGIN
        RAISERROR ('Not enough seats available for this tour.', 16, 1);
    END
    ELSE
    BEGIN
        INSERT INTO Reservations (reservationID, accountID, tourID, reserved_seats)
        SELECT reservationID, accountID, tourID, reserved_seats
        FROM INSERTED;
    END
END;
```

## Sprawdzenie przed dodaniem zarezerwowanej atrakcji czy jest miejsce - Michał Zięba

```sql=
CREATE TRIGGER CheckMaxParticipants
ON ReservedAttractions
INSTEAD OF INSERT
AS
BEGIN
    IF EXISTS (
        SELECT
            RA.tour_attractionID,
            COUNT(*) AS current_participants,
            TA.max_participants
        FROM
            ReservedAttractions RA
            INNER JOIN TourAttractions TA ON RA.tour_attractionID = TA.tour_attractionID
        WHERE
            RA.tour_attractionID IN (SELECT tour_attractionID FROM inserted)
        GROUP BY
            RA.tour_attractionID, TA.max_participants
        HAVING
            COUNT(*) + (SELECT COUNT(*) FROM inserted WHERE tour_attractionID = RA.tour_attractionID) > TA.max_participants
    )
    BEGIN
        RAISERROR('Nie można dodać uczestnika do tej atrakcji - przekroczono maksymalną liczbę miejsc.', 16, 1);
    END
    ELSE
    BEGIN
        INSERT INTO ReservedAttractions (tour_attractionID, participantID)
        SELECT tour_attractionID, participantID FROM inserted;
    END
END;
```

## Usuwanie w tabelach PersonalClients/CompanyClients rekordu o tym samym id, gdy usunięty jest rekord w tabeli Accounts - Tomasz Leniart

```sql=
CREATE TRIGGER trg_DeleteAccount
ON dbo.Accounts
AFTER DELETE
AS
BEGIN
    IF EXISTS (SELECT 1 FROM deleted d INNER JOIN dbo.PersonalClients pc ON d.accountID = pc.accountID)
    BEGIN
        DELETE FROM dbo.PersonalClients 
        WHERE accountID IN (SELECT accountID FROM deleted);
    END

    IF EXISTS (SELECT 1 FROM deleted d INNER JOIN dbo.CompanyClients cc ON d.accountID = cc.accountID)
    BEGIN
        DELETE FROM dbo.CompanyClients 
        WHERE accountID IN (SELECT accountID FROM deleted);
    END
END;
```























































