# <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; ```