# Przykłady kodu - Podstawowe klasy ORM Praktyczne przykłady użycia podstawowych klas logiki biznesowej enova365/Soneta. ## Ważne zasady ### Thread-safety **Obiekty single-threaded** - nie współdziel między wątkami: - `Session`, `Module`, `Table`, `Row`, `Context` **Obiekty multi-threaded** - można współdzielić: - `BusApplication`, `Database`, `Login` Każdy wątek powinien tworzyć własną sesję (Login można współdzielić). ### Extension methods dla modułów Dostęp do modułów przez extension methods: ```csharp var tm = session.GetTowary(); var hm = session.GetHandel(); var crm = session.GetCRM(); var kadry = session.GetKadry(); var bm = session.GetBusiness(); ``` ### Transakcje biznesowe **Każda zmiana obiektu MUSI być w transakcji** `Session.Logout(editMode: true)`: ```csharp using (var transaction = session.Logout(editMode: true)) { // Zmiany: dodawanie, modyfikacja, kasowanie obiekt.Wlasciwosc = nowaWartosc; transaction.Commit(); // Zatwierdza zmiany } // Brak Commit() = automatyczny rollback session.Save(); // Zapis do bazy danych ``` ## Dostęp do danych ### Odczyt listy towarów ```csharp using Soneta.Business; using Soneta.Towary; public void WyswietlTowary(Login login) { // Sesja tylko do odczytu using (var session = login.CreateSession(true, false, "OdczytTowarow")) { var tm = session.GetTowary(); // Extension method // Iteracja po kluczu podstawowym (WgKodu) foreach (Towar t in tm.Towary.WgKodu) { Console.WriteLine($"{t.Kod}: {t.Nazwa}"); } } } ``` ### Wyszukiwanie po kluczu ```csharp public Towar ZnajdzTowar(Session session, string kod) { var tm = session.GetTowary(); // Wyszukiwanie po kluczu unikalnym return tm.Towary.WgKodu[kod]; } ``` ### Iteracja z filtrowaniem ```csharp public void WyswietlAktywneTowary(Session session) { var tm = session.GetTowary(); foreach (Towar t in tm.Towary.WgKodu) { // Filtrowanie w kodzie if (t.Typ == TypTowaru.Towar) { Console.WriteLine(t.Nazwa); } } } ``` ## Tworzenie obiektów ### Dodawanie nowego towaru ```csharp public void DodajTowar(Login login) { // Sesja edycyjna using (var session = login.CreateSession(false, false, "DodawanieTowaru")) { var tm = session.GetTowary(); // Transakcja biznesowa - wymagana! using (var transaction = session.Logout(editMode: true)) { // Utworzenie nowego obiektu var towar = new Towar(); // Dodanie do tabeli (zmiana stanu na Added) tm.Towary.AddRow(towar); // Ustawienie właściwości towar.Kod = "NOWY001"; towar.Nazwa = "Nowy towar"; towar.Typ = TypTowaru.Towar; transaction.Commit(); } // Zapisanie do bazy session.Save(); } } ``` ### Dodawanie dokumentu z pozycjami ```csharp public void UtworzFakture(Login login, Kontrahent kontrahentZInnejSesji, List<(Towar towar, int ilosc)> pozycjeZInnejSesji) { using (var session = login.CreateSession(false, false, "TworzenieFaktury")) { var hm = session.GetHandel(); // WAŻNE: Obiekty z innej sesji trzeba doczytać w bieżącej sesji! var kontrahent = session.Get(kontrahentZInnejSesji); // Cała operacja w jednej transakcji using (var transaction = session.Logout(editMode: true)) { // Utworzenie nagłówka dokumentu var faktura = new DokumentHandlowy(); hm.DokHandlowe.AddRow(faktura); faktura.Definicja = hm.DefDokHandlowe.WgSymbolu["FV"]; faktura.Kontrahent = kontrahent; faktura.Data = Date.Today; // Dodanie pozycji int lp = 1; foreach (var (towarZInnejSesji, ilosc) in pozycjeZInnejSesji) { // Doczytaj towar w bieżącej sesji var towar = session.Get(towarZInnejSesji); var poz = new PozycjaDokHandlowego(faktura); faktura.Pozycje.AddRow(poz); poz.Towar = towar; poz.Ilosc = new Quantity(ilosc, towar.Jednostka.Kod); poz.Lp = lp++; } transaction.Commit(); } session.Save(); } } ``` **WAŻNE:** W jednej sesji nie można mieszać obiektów z różnych sesji. Użyj `session.Get(obiekt)` aby doczytać obiekt w bieżącej sesji. ## Modyfikacja obiektów ### Aktualizacja pojedynczego obiektu ```csharp public void ZmienNazweTowaru(Login login, string kod, string nowaNazwa) { using (var session = login.CreateSession(false, false, "EdycjaTowaru")) { var tm = session.GetTowary(); var towar = tm.Towary.WgKodu[kod]; if (towar != null) { using (var transaction = session.Logout(editMode: true)) { towar.Nazwa = nowaNazwa; transaction.Commit(); } session.Save(); } } } ``` ### Aktualizacja z transakcją biznesową ```csharp public void AktualizujCeny(Login login, string nazwaCeny, decimal procentPodwyzki) { using (var session = login.CreateSession(false, false, "AktualizacjaCen")) { var tm = session.GetTowary(); foreach (Towar t in tm.Towary.WgKodu) { // Transakcja biznesowa - WYMAGANA dla każdej zmiany! using (var transaction = session.Logout(editMode: true)) { var cena = t.Ceny[nazwaCeny]; if (cena != null) { cena.Netto = new DoubleCy(cena.Netto.Value * (1 + procentPodwyzki / 100)); } transaction.Commit(); } } session.Save(); // Zapisuje wszystkie zmiany do bazy } } ``` ## Usuwanie obiektów ### Usuwanie obiektu ```csharp public void UsunTowar(Login login, string kod) { using (var session = login.CreateSession(false, false, "UsuwanieTowaru")) { var tm = session.GetTowary(); var towar = tm.Towary.WgKodu[kod]; if (towar != null) { using (var transaction = session.Logout(editMode: true)) { towar.Delete(); // Zmiana stanu na Deleted transaction.Commit(); } session.Save(); // Fizyczne usunięcie z bazy } } } ``` ## Praca z kontekstem ### Worker wyliczający właściwość ```csharp // Rejestracja workera na poziomie assembly [assembly: Worker] public class TowarWorker { [Context] public Magazyn MagazynFiltra { get; set; } [Context] public Towar Towar { get; set; } public decimal StanMagazynowy { get { if (MagazynFiltra != null) { return PoliczStan(Towar, MagazynFiltra); } return PoliczStanCalkowity(Towar); } } private decimal PoliczStan(Towar towar, Magazyn magazyn) { // Implementacja... return 0; } private decimal PoliczStanCalkowity(Towar towar) { // Implementacja... return 0; } } ``` ### Akcja workera w menu Czynności ```csharp // Rejestracja workera na poziomie assembly [assembly: Worker] public class WyslijEmailWorker { [Context] public Kontrahent[] Kontrahenci { get; set; } [Context] public Context Context { get; set; } [Action("Wyślij email")] public void Execute() { foreach (var k in Kontrahenci) { if (!string.IsNullOrEmpty(k.Email)) { WyslijEmail(k.Email); } } } private void WyslijEmail(string email) { // Implementacja... } } ``` ### Klasa parametrów (ContextBase) Klasa `ContextBase` jest przeznaczona do budowania klas parametrów, nie workerów: ```csharp public class FiltryTowarow(Context context) : ContextBase(context) { public Magazyn Magazyn { get; set; } [Caption("Typ towaru")] // Etykieta w UI, gdy inna niż nazwa property public TypTowaru? Typ { get; set; } public bool TylkoAktywne { get; set; } = true; } ``` **Uwaga:** W klasach parametrów atrybut `[Context]` nie jest wymagany. **Współdzielenie wartości przez Context** - wartości parametrów można przechowywać w obiekcie Context, co pozwala na współdzielenie między różnymi klasami parametrów: ```csharp public class FiltryTowarow(Context context) : ContextBase(context) { public Magazyn Magazyn { get => Context.GetOrDefault(); set => Context.Set(value); } [Caption("Typ towaru")] public TypTowaru? Typ { get => Context.GetOrDefault(); set => Context.Set(value); } } ``` ## Praca z GuidedRow ### Dostęp do historii zmian ```csharp public void PokazHistorie(Session session, Kontrahent kontrahent) { var bm = session.GetBusiness(); Console.WriteLine($"Historia zmian dla: {kontrahent.Nazwa}"); foreach (ChangeInfo ci in bm.ChangeInfos[kontrahent]) { Console.WriteLine($" {ci.Time}: {ci.Operator}"); } // Skróty Console.WriteLine($"Utworzono: {kontrahent.FirstChangeInfo?.Time}"); Console.WriteLine($"Ostatnia zmiana: {kontrahent.LastChangeInfo?.Time}"); } ``` ### Praca z załącznikami ```csharp public void DodajZalacznik(Login login, Towar towar, byte[] plik, string nazwa) { using (var session = login.CreateSession(false, false, "DodawanieZalacznika")) { // Doczytaj towar w bieżącej sesji var towarInSession = session.Get(towar); using (var transaction = session.Logout(editMode: true)) { var attachment = new Attachment(towarInSession, AttachmentType.Attachments); towarInSession.Module.Business.Attachments.AddRow(attachment); attachment.Name = nazwa; attachment.RawData = plik; transaction.Commit(); } session.Save(); } } public void WyswietlZalaczniki(Towar towar) { foreach (Attachment att in towar.Attachments) { Console.WriteLine($"- {att.Name} ({att.RawData.Length} bajtów)"); } // Domyślne zdjęcie using var defaultImage = towar.DefaultImage; if (defaultImage != null) { Console.WriteLine($"Zdjęcie główne: {defaultImage.FileName}"); } } ``` ## Dane konfiguracyjne vs operacyjne ### Odczyt danych konfiguracyjnych ```csharp // Odczyt pojedynczej wartości var opisDlaSzt = login.ExecuteConfig(configSession => configSession.GetTowary().Jednostki.WgKodu["szt"]?.Opis); // Odczyt listy wartości prostych public string[] PobierzKodyJednostek(Login login) { return login.ExecuteConfig(configSession => { var tm = configSession.GetTowary(); return tm.Jednostki.WgKodu.Select(j => j.Kod).ToArray(); }); } ``` **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. ```csharp public void OdczytKonfiguracji(Login login) { // Własna sesja konfiguracyjna - gdy potrzebny dostęp do obiektów using (var session = login.CreateSession(true, true, "Konfiguracja")) { var tm = session.GetTowary(); foreach (Jednostka j in tm.Jednostki.WgKodu) { Console.WriteLine($"{j.Kod}: {j.Opis}"); } } } ``` ### Modyfikacja danych konfiguracyjnych ```csharp public void DodajJednostke(Login login, string kod, string opis) { // Sesja edycyjna konfiguracyjna using (var session = login.CreateSession(false, true, "DodawanieJednostki")) { var tm = session.GetTowary(); using (var transaction = session.Logout(editMode: true)) { var jednostka = new Jednostka(); tm.Jednostki.AddRow(jednostka); jednostka.Kod = kod; jednostka.Opis = opis; transaction.Commit(); } session.Save(); } } ``` ## Pełny przykład - import towarów ```csharp public class ImportTowarow { public void Importuj(Login login, string sciezkaPliku) { var dane = WczytajZPliku(sciezkaPliku); using (var session = login.CreateSession(false, false, "ImportTowarow")) { var tm = session.GetTowary(); int dodano = 0; int zaktualizowano = 0; // Cały import w jednej transakcji using (var transaction = session.Logout(editMode: true)) { foreach (var wiersz in dane) { // Sprawdź czy towar istnieje var towar = tm.Towary.WgKodu[wiersz.Kod]; if (towar == null) { // Dodaj nowy towar = new Towar(); tm.Towary.AddRow(towar); towar.Kod = wiersz.Kod; dodano++; } else { zaktualizowano++; } // Ustaw/aktualizuj właściwości towar.Nazwa = wiersz.Nazwa; var cena = towar.Ceny["Hurtowa"]; cena.Netto = new DoubleCy(wiersz.Cena); } transaction.Commit(); } session.Save(); Console.WriteLine($"Import zakończony:"); Console.WriteLine($" Dodano: {dodano}"); Console.WriteLine($" Zaktualizowano: {zaktualizowano}"); } } private List WczytajZPliku(string sciezka) { // Implementacja wczytywania z CSV/Excel... return new List(); } private class DaneImportu { public string Kod { get; set; } public string Nazwa { get; set; } public decimal Cena { get; set; } } } ``` ## Obsługa błędów ### Wzorzec try-catch z sesją ```csharp public void BezpiecznaOperacja(Login login) { using (var session = login.CreateSession(false, false, "Operacja")) { try { var tm = session.GetTowary(); using (var transaction = session.Logout(editMode: true)) { // Operacje na danych... var towar = new Towar(); tm.Towary.AddRow(towar); towar.Kod = "TEST"; transaction.Commit(); } session.Save(); } catch (Exception ex) { // Logowanie błędu Console.WriteLine($"Błąd: {ex.Message}"); // Zmiany nie zostały zatwierdzone (brak Commit lub Save) // Sesja zostanie automatycznie zwolniona przez using } } } ``` ### Wzorzec z wieloma operacjami ```csharp public void WieleOperacji(Login login, List kody) { using (var session = login.CreateSession(false, false, "WieleOperacji")) { var tm = session.GetTowary(); var bledy = new List(); foreach (var kod in kody) { try { using (var transaction = session.Logout(editMode: true)) { var towar = tm.Towary.WgKodu[kod]; if (towar != null) { towar.Delete(); transaction.Commit(); } } } catch (Exception ex) { bledy.Add($"{kod}: {ex.Message}"); // Kontynuuj z następnym elementem } } session.Save(); // Zapisz udane operacje if (bledy.Any()) { Console.WriteLine("Błędy: " + string.Join(", ", bledy)); } } } ```