Files
soneta-erp-skills/soneta-programming/references/examples.md
T

16 KiB

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:

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):

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

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

public Towar ZnajdzTowar(Session session, string kod)
{
    var tm = session.GetTowary();
    
    // Wyszukiwanie po kluczu unikalnym
    return tm.Towary.WgKodu[kod];
}

Iteracja z filtrowaniem

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

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

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

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ą

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

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ść

// Rejestracja workera na poziomie assembly
[assembly: Worker<TowarWorker, Towar>]

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

// Rejestracja workera na poziomie assembly
[assembly: Worker<WyslijEmailWorker, Kontrahent>]

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:

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:

public class FiltryTowarow(Context context) : ContextBase(context)
{
    public Magazyn Magazyn
    {
        get => Context.GetOrDefault<Magazyn>();
        set => Context.Set(value);
    }
    
    [Caption("Typ towaru")]
    public TypTowaru? Typ
    {
        get => Context.GetOrDefault<TypTowaru?>();
        set => Context.Set(value);
    }
}

Praca z GuidedRow

Dostęp do historii zmian

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

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

// 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.

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

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

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<DaneImportu> WczytajZPliku(string sciezka)
    {
        // Implementacja wczytywania z CSV/Excel...
        return new List<DaneImportu>();
    }
    
    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ą

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

public void WieleOperacji(Login login, List<string> kody)
{
    using (var session = login.CreateSession(false, false, "WieleOperacji"))
    {
        var tm = session.GetTowary();
        var bledy = new List<string>();
        
        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));
        }
    }
}