Added: soneta-business-xml, soneta-programming-basics

This commit is contained in:
Marcin Wojas
2025-12-26 22:22:34 +01:00
parent 89763f77f1
commit 09c40f22dc
12 changed files with 3880 additions and 0 deletions
@@ -0,0 +1,643 @@
# 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<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
```csharp
// 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:
```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<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
```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<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ą
```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<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));
}
}
}
```