# Session, Login, Database, BusApplication

Dokumentacja klas zarządzających połączeniem z bazą danych i sesjami w platformie enova365/Soneta.

## Hierarchia obiektów

```
BusApplication (Singleton)
    └── Database[] (kolekcja baz danych)
         └── Login (uwierzytelniony użytkownik)
              └── Session[] (sesje robocze)
                   └── Module → Table → Row
```

## BusApplication

Singleton reprezentujący instancję aplikacji ERP. Tworzony podczas inicjalizacji systemu.

### Dostęp do instancji

```csharp
// Singleton
BusApplication app = BusApplication.Instance;

// Dostęp do bazy danych po nazwie
Database db = BusApplication.Instance["BazaDemo"];

// Iteracja po wszystkich bazach
foreach (Database db in BusApplication.Instance)
{
    Console.WriteLine(db.Name);
}
```

### Właściwości

| Właściwość | Typ | Opis |
|------------|-----|------|
| `Instance` | `BusApplication` | Statyczny singleton |
| `Is365` | `bool` | `true` = wersja HTML, `false` = wersja okienkowa |
| `this[string]` | `Database` | Indeksator - baza po nazwie |

## Database

Abstrakcyjna klasa reprezentująca bazę danych. Konkretne implementacje dla wspieranych silników:

```
Database (abstrakcyjna)
 └── SqlDatabase (abstrakcyjna)
      ├── MsSqlDatabase (SQL Server)
      └── AzureDatabase (Azure SQL)
```

**Uwaga:** MySqlDatabase oraz OracleDatabase nie są już obsługiwane.

### Konfiguracja

Obiekty Database są deserializowane z pliku `Lista baz danych.xml`.

### Właściwości

| Właściwość | Typ | Opis |
|------------|-----|------|
| `Name` | `string` | Nazwa bazy danych |
| `DefaultDatabase` | `bool` | Czy baza domyślna |
| `DatabaseName` | `string` | Nazwa bazy w silniku SQL (SqlDatabase) |

### Logowanie do bazy

```csharp
Database db = BusApplication.Instance["BazaDemo"];

// ZALECANE: Logowanie z LoginParameters
Login login = db.Login(new LoginParameters
{
    UserName = "Administrator",
    UserPassword = "password"
});

// Logowanie zintegrowane Windows
Login login = db.Login(new LoginParameters
{
    UserName = LoginParameters.WindowsAuthenticationUser
});
```

## Login

Obiekt reprezentujący zalogowanego użytkownika. Zarządza sesjami.

### Tworzenie

```csharp
Database db = BusApplication.Instance["BazaDemo"];
Login login = db.Login(new LoginParameters
{
    UserName = "Administrator",
    UserPassword = "password"
});
```

### Dostęp do informacji o operatorze

**WAŻNE:** Właściwości `Operator` i `Entitle` w klasie `Login` nie należy stosować, ponieważ używają obiektu z `ConfigSession` w sposób niekontrolowany.

```csharp
// NIE ZALECANE:
// var op = login.Operator;  // unikać!
// var ent = login.Entitle;  // unikać!

// ZALECANE: Użyj w konkretnej sesji
using (var session = login.CreateSession(true, false, "Odczyt"))
{
    var op = session.AuthorizationInfo.Operator;
    Console.WriteLine($"Zalogowany: {op.Name} - {op.FullName}");
}
```

### Właściwości informacyjne

| Właściwość | Typ | Opis |
|------------|-----|------|
| `IsWebUser` | `bool` | Czy operator pulpitu (web) |
| `Licencje` | `IEnumerable` | Pobrane licencje |
| `Database` | `Database` | Baza danych logowania |

### Tworzenie sesji

```csharp
// ZALECANA sygnatura (czytelność + debugowanie)
Session session = login.CreateSession(
    readOnly: false,    // true = tylko odczyt
    config: false,      // true = dane konfiguracyjne
    name: "MojaSesja"   // nazwa sesji - pomocna przy debugowaniu
);
```

**Zawsze używaj wersji z parametrem `name`** - ułatwia debugowanie i identyfikację sesji.

### Dostęp do danych konfiguracyjnych

```csharp
// ZALECANE: ExecuteConfig z lambdą (zwraca wartość prostą)
var opisDlaSzt = login.ExecuteConfig(configSession =>
    configSession.GetTowary().Jednostki.WgKodu["szt"]?.Opis);

// NIE ZALECANE: login.ConfigSession
// var configSession = login.ConfigSession;  // unikać!
```

**WAŻNE:** Używając `ExecuteConfig()` nie można zwracać obiektów sesyjnych z sesji konfiguracyjnej, ponieważ może być używana w innym wątku do innych celów. Zwracaj tylko wartości proste lub kopie danych.

### Sygnatury CreateSession

```csharp
public Session CreateSession(bool readOnly, bool config, string name)
public Session CreateSession(bool readOnly, bool config)
public Session CreateSession()  // readOnly=false, config=false
```

## Session

Fundamentalna klasa do zarządzania danymi. **Każda operacja na danych wymaga sesji.**

### Tworzenie

```csharp
// ZAWSZE używaj using lub wywołuj Dispose()
using (var session = login.CreateSession(false, false, "MojaSesja"))
{
    // operacje na danych
    session.Save();
}
```

### Typy sesji - macierz

| ReadOnly | Config | Zastosowanie |
|----------|--------|--------------|
| `false` | `false` | **Edycja operacyjna** - dokumenty, kartoteki |
| `true` | `false` | **Odczyt operacyjny** - raporty, wyświetlanie |
| `false` | `true` | **Edycja konfiguracyjna** - ustawienia systemu |
| `true` | `true` | **Odczyt konfiguracyjny** - odczyt słowników |

**WAŻNE:** W sesji operacyjnej (`config: false`) nie można modyfikować obiektów konfiguracyjnych. Do modyfikacji konfiguracji wymagana jest sesja konfiguracyjna (`config: true`).

### Właściwości

| Właściwość | Typ | Opis |
|------------|-----|------|
| `ReadOnly` | `bool` | Czy sesja tylko do odczytu |
| `IsConfig` | `bool` | Czy sesja konfiguracyjna |
| `Name` | `string` | Nazwa sesji |
| `Login` | `Login` | Obiekt logowania |
| `Database` | `Database` | Baza danych |

### Metody zarządzania danymi

```csharp
// Zapisanie zmian do bazy
session.Save();
```

### Dane operacyjne vs konfiguracyjne

**Dane operacyjne (`config=false`):**
- Dokumenty, kartoteki, transakcje
- Zawsze odczytywane z bazy (aktualność)
- Modyfikacje zapisywane natychmiast przy `Save()`

**Dane konfiguracyjne (`config=true`):**
- Słowniki, definicje, ustawienia
- Buforowane w cache (optymalizacja)
- Modyfikowane rzadko, odczytywane często
- Określane atrybutem `config="true"` w business.xml

### Wiele sesji jednocześnie

```csharp
// Normalne zjawisko - wiele sesji może współistnieć
using (var session1 = login.CreateSession(true, false, "Lista1"))
using (var session2 = login.CreateSession(true, false, "Lista2"))
{
    // Obie sesje mogą odczytywać te same dane
    var tm1 = session1.GetTowary();
    var tm2 = session2.GetTowary();
}
```

### Transakcje biznesowe - Session.Logout()

**WAŻNE:** Każda zmiana obiektu biznesowego MUSI być w transakcji!

```csharp
using (var session = login.CreateSession(false, false, "Edycja"))
{
    var tm = session.GetTowary();
    var towar = tm.Towary.WgKodu["KOD001"];
    
    // Logout(editMode: true) - transakcja EDYCYJNA (można modyfikować)
    using (var transaction = session.Logout(editMode: true))
    {
        towar.Nazwa = "Nowa nazwa";
        var cena = towar.Ceny["Hurtowa"];
        cena.Netto = new DoubleCy(100.00m);
        transaction.Commit();  // Zatwierdza zmiany w sesji
    }
    // Brak Commit() = rollback (przywrócenie stanu sprzed Logout)
    
    session.Save();  // Zapisuje do bazy danych
}
```

### Logout(editMode: false) - transakcja tylko do odczytu

```csharp
using (var session = login.CreateSession(false, false, "Przeglad"))
{
    var tm = session.GetTowary();
    var towar = tm.Towary.WgKodu["KOD001"];
    
    // Transakcja tylko do odczytu
    using (var readTrans = session.Logout(editMode: false))
    {
        // Tutaj NIE można modyfikować
        
        // Ale można otworzyć zagnieżdżoną transakcję edycyjną
        using (var editTrans = session.Logout(editMode: true))
        {
            var cena = towar.Ceny["Hurtowa"];
            cena.Netto = new DoubleCy(200m);
            editTrans.Commit();
        }
        
        // WAŻNE: Commit wymagany też dla transakcji readonly!
        // Inaczej zmiany z zagnieżdżonych transakcji edycyjnych przepadną
        readTrans.Commit();
    }
    session.Save();
}
```

| Metoda | Opis |
|--------|------|
| `session.Logout(editMode: true)` | Transakcja edycyjna - można modyfikować |
| `session.Logout(editMode: false)` | Transakcja tylko do odczytu |
| `transaction.Commit()` | Zatwierdza zmiany (wymagane dla obu typów!) |
| `transaction.CommitUI()` | Zatwierdza + odświeża UI |
| `transaction.Dispose()` | Bez Commit = rollback (także zagnieżdżonych zmian) |

## Kompletny przykład

```csharp
// 1. Pobranie instancji aplikacji
BusApplication app = BusApplication.Instance;

// 2. Pobranie bazy danych
Database db = app["MojaBaza"];

// 3. Logowanie
Login login = db.Login(new LoginParameters
{
    UserName = "admin",
    UserPassword = "haslo"
});

// 4. Utworzenie sesji (zawsze z nazwą!)
using (var session = login.CreateSession(false, false, "Import"))
{
    // 5. Pobranie modułu (extension method)
    var tm = session.GetTowary();
    
    // 6. Transakcja biznesowa - WYMAGANA dla zmian!
    using (var transaction = session.Logout(editMode: true))
    {
        var nowyTowar = new Towar();
        tm.Towary.AddRow(nowyTowar);
        nowyTowar.Kod = "IMPORT001";
        nowyTowar.Nazwa = "Zaimportowany towar";
        
        transaction.Commit();
    }
    
    // 7. Zapis do bazy
    session.Save();
}
// 8. Session automatycznie Dispose() przez using
```

## Thread-safety

### Obiekty multi-threaded (można współdzielić między wątkami)
- `BusApplication`
- `Database`
- `Login`

### Obiekty single-threaded (NIE współdzielić)
- `Session`, `Module`, `Table`, `Row`, `Context`

```csharp
// DOBRZE - Login można współdzielić, każdy wątek tworzy własną sesję
Login sharedLogin = db.Login(new LoginParameters { UserName = "admin", UserPassword = "haslo" });

Parallel.ForEach(items, item => {
    using (var session = sharedLogin.CreateSession(false, false, "Watek"))
    {
        var tm = session.GetTowary();
        using (var transaction = session.Logout(editMode: true))
        {
            // operacje...
            transaction.Commit();
        }
        session.Save();
    }
});
```

## Sesja bez interfejsu graficznego

Sesja działa w warstwie logiki biznesowej - **nie wymaga UI**.

Przykłady użycia bez interfejsu:
- **Harmonogram Zadań** - usługa Windows wykonująca operacje bazodanowe
- **Importy danych** - procesy wsadowe
- **Testy jednostkowe** - automatyczne testy logiki
- **API REST** - obsługa żądań HTTP
