Soneta Programming Basics - Podstawowe klasy ORM
Skill zawiera dokumentację fundamentalnych klas logiki biznesowej platformy Soneta. Klasy te stanowią podstawę mapowania obiektowo-relacyjnego (ORM) i są niezbędne do tworzenia kodu i dodatków.
Code review / refaktoring: po napisaniu nowego kodu biznesowego oraz po każdym refaktoringu, zawsze zweryfikuj go względem references/safe-code.md. Ten sam dokument służy jako lista kontrolna do review PR-ów.
Mapa skilla
SKILL.md zawiera "duży obraz" - hierarchię klas, thread-safety, kanoniczne wzorce. Po szczegóły konkretnego tematu sięgaj do referencji:
| Temat |
Gdzie szukać |
Nowy dodatek od zera — wygenerowanie szkieletu źródeł przez CLI (dotnet new soneta-addon, Soneta.MsBuild.SDK, global.json/Directory.Build.props, solucja, debug VS Code) |
references/new-addon-cli.md |
| Hierarchia ORM, Row / Table / Module, klucze, ISessionable |
sekcje poniżej |
Implementacja klas Row/Table, konstruktory (pola readonly, RowCreator), selector + [BusinessRow], [NewRow], [DefaultConstructor], jawne wartości enum'ów, zdarzenia cyklu życia (OnAdded/OnLoaded/OnEditing/OnDeleting/OnDeleted/OnRepacked — override z base.OnXxx(); alternatywa bez dziedziczenia: statyczne delegaty XSchema) |
references/row-types.md |
AssemblyAttributes - odczyt atrybutów z załadowanych modułów (GetCustom<T>, Find, iteracja po assembly, cache, analiza DLL w runtime) |
references/assembly-attributes.md |
| Sesje, transakcje, Login, Database, BusApplication, optimistic locking |
references/session-login.md |
| Paczki danych, Datapack, GuidedRow, ExportedRow, synchronizacja, blokady |
references/datapack-guidedrow.md |
| Klasa Context - dane z UI, zaznaczenia, parametry workera |
references/context.md |
Klasy parametrów (ContextBase) - filtry, trwałość, InvokeChanged (ta sama konstrukcja służy jako parametry wydruków → /soneta-repx) |
references/contextbase.md |
Obiekty Worker i Extender - rozszerzenia modelu, akcje w menu Czynności ([Action] i dynamiczne GetActions), rozszerzanie obiektów obcych (property/kolekcje dodatku na obcym typie Soneta bez modyfikacji jego kodu) |
references/worker-extender.md |
| Serwisy biznesowe (App / Database / Login / Session scope), rejestracja warunkowa ServiceInitializer (per instancja właściciela, np. per silnik bazy; kolejność po Priority) |
references/services.md |
Tłumaczenia (Translate, TranslateIgnore), ILogger, ActSource, lokalizacja plików logów (katalog Soneta/Logs/ — macOS: ~/Library/Application Support/, Windows: %APPDATA%\; pliki server-*/dbmgr-*/test-*) |
references/translations-logging.md |
| Action result (rezultaty workera) zwracany przez worker / extender / Command - raporty, dialogi, nawigacja |
references/action-result.md |
| RowCondition - serwerowe warunki LINQ, filtrowanie SubTable / View / Query |
references/rowcondition.md |
ViewInfo - definicja widoków list (folderów i inline jako property), CreateView, args.DataSource, klasa Params, [Accessor(AutoChange)], powiązanie z viewform.xml |
references/viewinfo.md |
ChangeInfos - dziennik zmian / audyt (session.ChangeInfos.Add, pola Info/Data, pułapka 255 znaków, ChangeInfoType, prezentacja listy) |
references/changeinfos.md |
| Cechy (Features) - tabela Features, typy cech, dostęp typowany/nietypowany, bindowanie w form.xml |
references/features.md |
Weryfikatory - walidacja spójności danych (Verifier, RowVerifier<T>, ColVerifier<T>, MultiColVerifier<T>, RequiredVerifier), poziomy Error/Warning/Information, uzbrajanie na zmianę pól-źródeł (Session.Verifiers.Add, <verifier> w business.xml), blokada Save() przy błędzie |
references/verifiers.md |
Eventy (zdarzenia sesji) - odraczanie ciężkich obliczeń i logika w transakcji serwerowej: sesyjne (Session.Events, odpalane na CommitUI/Save/Invoke) vs serwerowe (Session.ServerEvents, w transakcji Save — numeracja). Rejestracja Add(handler[, args]), dedup po Equals/GetHashCode, wymuszanie Invoke(handler), argumenty (BusEventArgs, RowEventArgs<TRow>, SessionBusEventArgs), priorytety, transakcyjność |
references/events.md |
Źródła praw (IRightsSource) - obiekt sterujący dostępem do danych operacyjnych, AccessRight, Login.GetObjectRight; nowe źródło domyślnie Denied, AccessDeniedException przy odczycie kolumn bez prawa, nadawanie praw i cache ról. Miejsce tabeli w drzewie uprawnień (pliki *.rightstree.xml, reguła korzenia praw, relacje relright/relguided) → skill /soneta-business-xml |
references/rights-source.md |
Notacja klamrowa (AccessorFormatter) - wstawki {ścieżka} / {ścieżka:format} w captionach, szablonach, promptach; metadane modeli (ApplicationInfo, TableInfo) |
references/metadata-formatting.md |
| Gotowe wzorce kodu end-to-end (import, CRUD, obsługa błędów) |
references/examples.md |
Receptury kodu per obiekt biznesowy (domena CRM) — Kontrahent (pola, kolekcje, workery, finanse, RODO, KSeF). Indeks + mapa receptur (CRM-W1–W18); rozdziały references/domeny/crm/CRM01..CRM10 |
references/domeny/crm.md |
Receptury kodu per obiekt biznesowy (domena Handel) — DokumentHandlowy (faktury/magazynowe/zamówienia/korekty, relacje IRelacjeService, cykl życia, magazyn/partie/obroty, VAT/waluty, płatności, KSeF/fiskal/Intrastat, wydruki). Indeks + mapa receptur (HANDEL-W1–W82); rozdziały references/domeny/handel/HANDEL01..HANDEL14 |
references/domeny/handel.md |
Receptury kodu per obiekt biznesowy (domena Kadry-Płace) — Pracownik (zatrudnienie i dane kadrowe, historia PracHistoria+Etat, dodatki, nieobecności/limity, plan pracy/RCP, umowy cywilnoprawne, naliczanie wypłat, listy płac, wydruki PDF). Indeks + mapa receptur (KADRY-A*…K*); rozdziały references/domeny/kadry/KADRY01..KADRY11 |
references/domeny/kadry.md |
| Zasady bezpiecznego kodu biznesowego — checklist do review i refaktoringu |
references/safe-code.md |
Pola obiektów biznesowych — dane wygenerowane (data/props/) + skaner/regeneracja z DLL |
references/scan-props.md |
Moduły i tabele (*Module / *Row / *Table) — gotowy przegląd w data/props/ (INDEX.md → <Moduł>/INDEX.md) + skaner z DLL |
references/scan-modules.md |
Inwentaryzacja workerów i extenderów ([Worker<…>]) z DLL |
references/scan-workers.md |
Zakładki, sekcje danych i pola formularzy (zasoby *.pageform.xml/*.form.xml) z DLL — katalog data/forms/ (INDEX.md → <Przestrzeń>.md) + skaner na żądanie (DataContext/EditValue, Include, listy, kolejność pól pod kod i import XML) |
references/scan-forms.md |
Inwentaryzacja folderów statycznych menu ([assembly: FolderView]) z DLL — drzewo, listy, formularze |
narzędzie scan-folders w skillu /soneta-config |
Wydruki DevExpress (pliki .repx) — struktura raportu, źródło danych BusinessDataSource, rejestracja [assembly: DxReport(...)]; kod-behind wydruku (ReportSnippet/[DxBind], lub generyczny Snippet/[Bind] bez licencji DevExpress) liczący dane raportu w oparciu o ORM. Parametry wydruku konstruuje się jak parametry workerów — klasa ContextBase + [Context] (patrz wyżej) |
skill /soneta-repx (warstwa .repx) |
Testowanie na żywej aplikacji przez buscall call (CLI) — zdalne sterowanie programem (nawigacja, formularze, gridy, edycja) i zrzuty ekranu do analizy wizualnej; jednorazowe wywołania CLI bez zarządzania procesem (buscall --db <Baza> call <metoda> klucz=wartość), plus wariant MCP callmcp |
references/buscall-live-testing.md (weryfikacja na żywo) |
Import/eksport XML z kodu — SessionReader/SessionWriter — konstruktory (Session vs Login), obsługa błędów (CollectExceptions, zdarzenie ReaderException/ReaderResponse), mapowanie GUID-ów (AddGuidMap, GuidMapPolicy), tryby kolekcji (RelationsImportMode), eksport datapacku z subexports, ImportBusinessXml w testach; struktura samego pliku XML → artykuł import-export-xml w /soneta-config |
references/sessionreader-sessionwriter.md |
Testy integracyjne — klasa bazowa TestBase — testy na realnej bazie z automatycznym rollbackiem (dwupoziomowa transakcja), wybór bazy [TestDatabase] (nunit_default/nunit_ui/nunit_premiumui), cykl życia (ClassSetup/TestSetup), Session/ConfigSession/Context, InTransaction/SaveDispose, podmiana DI (ConfigureLoginServices), asercje AwesomeAssertions, testy SQL (SqlTraceInfo), konwencje: nazewnictwo Should_..._When_... + prefiks grupy, [Description], struktura AAA |
references/integration-tests.md |
Nowy dodatek od zera (CLI)
Gdy zaczynasz nowy dodatek (a nie modyfikujesz istniejący), najpierw wygeneruj szkielet źródeł
z wiersza poleceń, a dopiero potem wypełniaj go treścią. Robi to paczka szablonów Soneta Platform
Developer w oparciu o Soneta.MsBuild.SDK — jednym poleceniem powstają rozdzielone projekty
(logika biznesowa, UI, testy) już skonfigurowane pod platformę Soneta:
dotnet new install Soneta.Platform.Developer # raz na maszynę (starsza składnia: dotnet new -i …)
dotnet new soneta-addon -n MyExtension -o ./MyExtension
# >>> obowiązkowy krok konfiguracji (patrz niżej) — nadpisz 3 pliki poprawną treścią <<<
cd ./MyExtension && dotnet build # weryfikacja
Powstaje gotowa solucja MyExtension.sln z projektami MyExtension (logika), MyExtension.UI,
MyExtension.Tests oraz pliki global.json (wersja SDK) i Directory.Build.props (wersja bibliotek
SonetaPackageVersion). SDK sam pobiera biblioteki Soneta i uruchamia generator
*.business.xml → *.business.cs przy buildzie. Dodatki celują w net10.0 i są cross-platform
— pełny cykl (generowanie, build, testy) działa na macOS/Linux/Windows.
Obowiązkowy krok po wygenerowaniu — nadpisz pliki konfiguracyjne poprawną treścią. Szablon
1.0.7 generuje konfigurację, która nie buduje się z pudełka, więc zaraz po dotnet new (a przed
dotnet build) doprowadź trzy pliki do poprawnej postaci — nie czekaj, aż build padnie:
global.json — przypnij "Soneta.Sdk": "1.2.0" (szablon wpisuje starszą, np. 1.1.8).
Directory.Build.props — dodaj w pierwszym PropertyGroup
<SonetaTargetFramework>net10.0</SonetaTargetFramework> (szablon zostawia tę zmienną pustą).
NuGet.Config — utwórz w katalogu solucji z feedem Soneta
(https://nuget.soneta.pl/v3/index.json); szablon nie generuje tego pliku.
Gotowa treść wszystkich trzech plików: references/new-addon-cli.md.
Dopiero po tym uruchom dotnet build (powinien przejść z 0 ostrzeżeń, 0 błędów).
dotnet build = walidator obu warstw (generacja business.xml → business.cs i C#) — jednym
poleceniem wychodzą błędy generatora i braki klas konkretnych; to najszybsza pętla iteracji.
W raportach rozróżniaj „zweryfikowane buildem" vs „wymaga weryfikacji runtime" (żywa aplikacja /
testy) — zielony build ≠ działające runtime.
Kryterium ukończenia: w katalogu docelowym istnieją trzy *.csproj, global.json
i Directory.Build.props. Dalej wypełniasz szkielet: definicje (soneta-business-xml) → kod
biznesowy (ten skill) → UI (soneta-form-xml).
Pełna procedura — szablony, pliki konfiguracyjne, parametry MSBuild, solucja, debug w VS Code,
referencje WinForms, ręczny fallback bez szablonów i troubleshooting:
references/new-addon-cli.md.
Architektura warstw
BusApplication.Instance (singleton) - multithreaded
└── Database
└── Login
└── Session - single-threaded
└── Module
└── Table
└── Row
3 poziomy logiki biznesowej
| Poziom |
Opis |
Przykłady klas |
| 1. Bazowe |
Klasy wspólne dla wszystkich modułów (Soneta.Business.dll) |
Row, Table, Module, Session, Context |
| 2. Generowane |
Klasy generowane przez BusinessGenerator z *.business.xml (sufiksy: Row, Table, Module) |
TowarRow, TowarTable, TowaryModule |
| 3. Implementowane |
Klasy konkretne tworzone przez programistę |
Towar, Towary (bez sufiksów) |
BusinessGenerator jest automatycznie uruchamiany podczas kompilacji dla plików *.business.xml. Szczegółowy opis definiowania business.xml znajduje się w skill soneta-business-xml.
Klasy poziomu 3 (Towar, Towary) pisze programista, dziedzicząc po klasach generowanych.
Gdy tabela ma pola readonly (w tym selector), klasa obiektu biznesowego wymaga
dodatkowych konstruktorów; selector pozwala też przechowywać wiele typów obiektów w jednej
tabeli. Wzorce implementacji (konstruktory, [BusinessRow], [NewRow]) —
references/row-types.md.
Hierarchia głównych klas
Row (abstrakcyjna)
└── GuidedRow (+ Guid, Attachments, ChangeInfos)
└── ExportedRow (+ Exported flag - rozróżnia dane konfiguracyjne od operacyjnych)
Table (abstrakcyjna)
└── GuidedTable (indeksator po Guid)
└── ExportedTable
Module (abstrakcyjna)
└── [NazwaModulu]Module (np. TowaryModule)
Szczegóły GuidedRow / ExportedRow (flaga Exported, atrybuty guided / relguided w business.xml, ChangeInfos, blokady) - patrz references/datapack-guidedrow.md.
Thread-safety
Obiekty single-threaded (NIE współdzielić między wątkami)
Session
Module
Table
Row
Context
- oraz wszystkie klasy pochodne
Obiekty multi-threaded (można współdzielić)
BusApplication
Database
Login
Klasa Session - fundamenty
Session to kluczowa klasa do zarządzania danymi. Każda operacja na danych wymaga sesji. Sesja jest single-threaded i implementuje IDisposable - zawsze opakowuj w using.
Session session = login.CreateSession(readOnly: false, config: false, name: "MojaSesja");
Każda modyfikacja obiektu MUSI być w transakcji biznesowej otwieranej przez Session.Logout(editMode: true) - dotyczy dodawania, modyfikacji właściwości oraz kasowania:
using (var transaction = session.Logout(editMode: true))
{
towar.Nazwa = "Zmieniona nazwa";
transaction.Commit(); // Commit() w kodzie biznesowym
// transaction.CommitUI(); // CommitUI() w kodzie UI (worker, extender, Command)
}
session.Save(); // zapis do bazy - optimistic-lock; konflikty wykrywane tu
Brak Commit() = automatyczny rollback przy Dispose() - dotyczy też transakcji tylko do odczytu.
Pełna dokumentacja (typy sesji edycyjna / readonly / konfiguracyjna, transakcje zagnieżdżone, Commit vs CommitUI, optimistic locking, mieszanie obiektów z różnych sesji przez session.Get(obiekt)) - patrz references/session-login.md.
Klasa Module
Moduł grupuje logicznie powiązane tabele. Nie ma odwzorowania w bazie danych.
**Preferowany odczyt modułu to metoda rozszerzająca session.GetX() (generowanymi z business.xml, także dla modułów dodatków). ** Wariant
Module.GetInstance(ISessionable) stosuj tylko, gdy nie masz dostępu do property Session.
Nie używaj ogólnego session.Get<TModule>() ani session.Modules[...] — zasada
§14.4 w safe-code.md.
// Dostęp do modułu - extension method (preferowane)
var tm = session.GetTowary();
var hm = session.GetHandel();
var crm = session.GetCRM();
var bm = session.GetBusiness();
// Dostęp do tabel
Towary towary = tm.Towary;
Jednostki jednostki = tm.Jednostki;
// Moduł implementuje ISessionable
Session s = tm.Session;
Klasa Table
Reprezentuje tabelę w bazie danych. Udostępnia dostęp do kolekcji wierszy.
var towary = tm.Towary;
// Iteracja po kluczu podstawowym
foreach (Towar t in towary.WgKodu) { ... }
// Iteracja po innym kluczu
foreach (Towar t in towary.WgNazwy) { ... }
// Właściwości
Table.AccessRight // Prawa dostępu
Table.Session // Sesja (ISessionable)
Table.Module // Moduł nadrzędny
Table.PrimaryKey // Klucz podstawowy
Klasa Row
Reprezentuje pojedynczy wiersz (rekord) z tabeli.
Właściwości bazowe
Row.ID // int - PODSTAWOWY identyfikator obiektu w tabeli (autoincrement, Primary Key)
Row.State // RowState - stan obiektu w sesji
Row.Table // Table - tabela nadrzędna
Row.Module // Module - moduł
Row.Session // Session - sesja
ID jest automatycznie generowany przez bazę danych i stanowi klucz główny (Primary Key) tabeli.
Stany obiektu (RowState)
| Stan |
Opis |
Detached |
Nowy obiekt, nie przypisany do tabeli |
Unchanged |
Wczytany z bazy, bez zmian |
Modified |
Zmodyfikowany w sesji |
Added |
Nowy, dodany do tabeli, nie zapisany w bazie |
Deleted |
Skasowany, do usunięcia z bazy |
Klucze i indeksy
Definiowane w *.business.xml, mapowane na indeksy w bazie danych.
<key name="WgKodu" keyunique="true" keyprimary="true">
<keycol name="Kod"/>
</key>
| Atrybut |
Znaczenie |
keyprimary="true" |
Klucz podstawowy (domyślne sortowanie) |
keyunique="true" |
Wartości unikalne w tabeli |
Uwaga: keyprimary w business.xml to nie to samo co Primary Key w bazie (który jest zawsze na kolumnie ID).
Interfejs ISessionable
public interface ISessionable {
Session Session { get; }
}
Implementują go: Session, Module, Table, Row, Context, Key.
Używany jako argument funkcji wymagających kontekstu sesji:
// Metoda statyczna GetInstance - akceptuje ISessionable
public static TowaryModule GetInstance(ISessionable session)
// Extension method - wygodniejsza składnia (tylko dla Session)
public static TowaryModule GetTowary(this Session session)
Zalecane użycie:
// Extension method (prostsze)
var tm = session.GetTowary();
var tm = context.Session.GetTowary();
var tm = towar.Session.GetTowary();
// GetInstance (gdy mamy ISessionable, ale property Session jest niedostępne)
var tm = TowaryModule.GetInstance(sessionable);
Kod biznesowy vs UI
Kod biznesowy realizuje operacje logiki biznesowej (jak backend). Kod UI (frontend) jest odpowiedzialny za prezentację danych i interakcję z użytkownikiem. Kod biznesowy może być umieszczony w tej samej klasie z kodem UI.
Kod UI to np.:
- obiekty
View, ViewInfo, extender
- metody sterujące
IsReadOnlyXxx, IsVisibleXxxx, GetListXxx, IsEnabledXxx, GetNameXxx, GetAppearanceXxx
Ważne zasady do stosowania w kodzie biznesowym
- Nie używaj żadnych obiektów kodu UI, w szczególności
View - zamiast tego możesz użyć SubTable[condition].
- Nie należy stosować warunków na prawa dostępu (np.
if (Table.AccessRight == AccessRights.Denied) {...}).
Szczegóły konstruowania ViewInfo (atrybut FolderView, eventy CreateView/InitContext, klasa Params, powiązanie z viewform.xml) opisuje references/viewinfo.md.
Szybki start - wzorce kodu
Kanoniczny wzorzec transakcji biznesowej (Logout → zmiana → Commit → Save) pokazuje
sekcja Klasa Session - fundamenty powyżej. Gotowe wzorce
end-to-end (odczyt, tworzenie, modyfikacja, kasowanie, obsługa błędów, pełny import) -
patrz references/examples.md.
Gotowe receptury per obiekt biznesowy (realne pola, kolekcje i workery) są w references/domeny/:
crm.md (Kontrahent), handel.md
(DokumentHandlowy), kadry.md (Pracownik),
workflow.md (procesy i zadania).
Narzędzia pomocnicze
Skill udostępnia skrypty dotnet script (scripts/) do statycznej inwentaryzacji bibliotek Soneta — bez ładowania IL do CLR (metadane typów: Roslyn MetadataReference.CreateFromFile; zasoby osadzone: System.Reflection.Metadata):
- Pola tabel — najpierw dane wygenerowane. Kontrakty pól wszystkich ~1200 tabel są już wyeksportowane do
data/props/ (plik <Moduł>/<RowType>.md na tabelę). Odczyt jest natychmiastowy — nie skanuj DLL, gdy tabela tam jest. Znasz RowType? Nie czytaj indeksów, tylko ls data/props/*/DokumentHandlowy.md (pełny zestaw poleceń niżej). Jak regenerować po zmianie wersji: references/scan-props.md.
- Moduły i tabele — indeks dwupoziomowy.
data/props/INDEX.md to router: statystyki, polecenia wyszukiwania i lista 37 modułów. Inwentaryzacja tabel modułu jest w data/props/<Moduł>/INDEX.md (RowType | Tytuł | Tabela | Konfig | Guided | Historia | Interfaces | Selektor | Plik; kolumna Historia: historyczna → H / historia → P). Podział jest celowy — monolityczny indeks (212 KB) przekraczał limit jednego odczytu.
- Interfejsy —
data/props/Interfaces.md. Wszystkie interfejsy z [TableInfo(Interfaces=...)] i tabele je implementujące (relacje interfejsowe w jednym miejscu, tabele linkowane do plików).
scan-modules.csx — skaner na żądanie listujący moduły i tabele z DLL (dla innego katalogu DLL niż ten, z którego zbudowano data/props/). Szczegóły: references/scan-modules.md.
export-props-all.csx — wsadowo regeneruje cały data/props/ z DLL (kompilacja budowana raz, ~1200 tabel w kilka sekund). Uruchamiaj po zmianie wersji platformy lub przebudowie dodatku. Szczegóły: references/scan-props.md.
scan-props.csx — skaner na żądanie dla pojedynczej tabeli (fallback, gdy tabeli nie ma w data/props/). Wypisuje pola i właściwości kalkulowane klasy biznesowej, rekurencyjnie po polach typu subrow, oraz — dla tabel z selektorem — sekcję podtypów ([BusinessRow]/[NewRow]). Szczegóły: references/scan-props.md.
scan-workers.csx — wypisuje na stdout JSON z workerami i extenderami zarejestrowanymi atrybutem assembly [Worker<…>], pogrupowanymi wg DataType. Dla każdej klasy: parametry inicjowane z Context (ctor + [Context], z rozwinięciem pod-property dla typów dziedziczących z ContextBase), property do bindowania, akcje menu Czynności. Opcjonalny drugi argument filtruje wynik do konkretnego typu danych (np. DokumentHandlowy) — w praktyce konieczny, bo pełne skanowanie zwraca tysiące rejestracji. Wynik łatwo przetwarzać jq. Szczegóły: references/scan-workers.md.
- Formularze — najpierw INDEX. Katalog wszystkich ~5700 zakładek (pageform) jest wyeksportowany do
data/forms/: INDEX.md to router (sposoby wyszukiwania, lista 26 przestrzeni nazw, sekcja systemowa), a zakładki biznesowe leżą w data/forms/<Przestrzeń>.md (Typ danych | Zakładka (plik) | Nazwa zakładki | Priority | Biblioteka | Przestrzeń, sortowane po typie). Nie musisz znać przestrzeni — rg '^\| Kontrahent \|' data/forms/*.md przeszukuje wszystkie naraz. Typ = segment przed nazwą zakładki w …<TYP>.<ZAKŁADKA>.pageform.xml (albo DataType). Zakładki typów ogólnych (Row/IRow/…) wydzielone jako systemowe (osobna sekcja routera; scan-forms nie raportuje ich per obiekt). Odczyt natychmiastowy, bez DLL. Regeneracja: export-forms-index.csx. Szczegóły: references/scan-forms.md.
scan-forms.csx — skaner na żądanie dla pojedynczego obiektu: odczytuje z zasobów osadzonych (*.pageform.xml/*.form.xml) zakładki, sekcje danych (grupy) i pola w kolejności wprowadzania, rozwijając ścieżki pól przez łańcuch DataContext/EditValue, dołączane Include (także między bibliotekami) oraz listy (Grid/Scheduler/… → kolumny elementu kolekcji, separator : między wczytaniem listy a polami elementu). Argument = prefiks nazwy pliku lub DataType/Namespace.Typ. Do budowania danych kodem i importu XML business="true". Szczegóły: references/scan-forms.md.
export-forms-index.csx — wsadowo generuje data/forms/INDEX.md i pliki przestrzeni z DLL (jeden przebieg, ~5700 zakładek w ~1 s). Uruchamiaj po zmianie wersji platformy lub przebudowie dodatku.
Jak szukać w danych wygenerowanych
Dane w data/ są tak zorganizowane, że nazwa pliku niesie informację — najczęściej
wystarczy ls albo jeden rg, bez czytania indeksów. Polecenia uruchamiaj z katalogu skilla:
# Pola tabeli, gdy znasz RowType — plik nazywa się <Moduł>/<RowType>.md
ls data/props/*/DokumentHandlowy.md # → data/props/Handel/DokumentHandlowy.md
ls data/props/*/*Pracownik*.md # gdy nie znasz dokładnej nazwy
# Tabela po nazwie w bazie (kolumna `Tabela`) lub po tytule
rg -l '`DokHandlowe`' data/props/*/INDEX.md
# Które tabele mają pole wskazujące na dany obiekt (relacje)
rg -l 'Soneta\.CRM\.Kontrahenci\.Kontrahent`' data/props/*/*.md
# Zakładki obiektu — bez znajomości przestrzeni nazw
rg '^\| Kontrahent \|' data/forms/*.md
# Które tabele implementują interfejs
rg '^\| `IDokument' data/props/Interfaces.md
Nie czytaj data/props/<Moduł>/INDEX.md ani data/forms/<Przestrzeń>.md w całości, gdy
szukasz jednego obiektu — to setki wierszy. Indeksy służą do przeglądu modułu, rg/ls do
trafienia w konkret.
Inwentaryzację folderów statycznych menu ([assembly: FolderView]) — drzewo pozycji menu, list i formularzy — realizuje narzędzie scan-folders przeniesione do skilla /soneta-config (komplementarne do scan-modules: perspektywa funkcjonalno-użytkowa zamiast danych).
Konwencje nazewnicze
| Element |
Konwencja |
Przykład |
| Klasa wiersza (Row) |
PascalCase, l.poj. |
Towar, Kontrahent |
| Klasa tabeli |
PascalCase, l.mn. |
Towary, Kontrahenci |
| Klasa modułu |
Nazwa + Module |
TowaryModule |
| Klucz |
Wg + nazwa kolumny |
WgKodu, WgNazwy |
| Namespace |
Soneta.NazwaModułu |
Soneta.Towary |
Język identyfikatorów
| Typ |
Język |
Przykłady |
| Logika biznesowa |
polski |
Towar, Kontrahent, DokumentHandlowy, Faktura |
| Identyfikatory systemowe |
angielski |
Session, Context, Row, Table, Module |
Można łączyć polski i angielski w nazwach metod i klas:
RetrieveTowary()
UpdateKontrahent()
GetDokumentyHandlowe()
CreateFaktura()
1---2name: soneta-programming3description: Klasy ORM i wzorce kodu biznesowego platformy Soneta (enova365, Soneta Enterprise, Triva). Używaj gdy użytkownik: (1) pisze, modyfikuje lub refaktoruje kod biznesowy enova365/Soneta/Triva (Row/Table/Module, sesje i transakcje, selector, typy wierszy); (2) pyta o konkretny mechanizm ORM — Session, Commit/Save, optimistic lock, Context, RowCondition, Datapack, ViewInfo, Features, thread-safety; (3) prosi o code review kodu biznesowego Soneta (safe-code); (4) pisze worker, extender, akcję w menu Czynności, folder/listę; (5) chce zinwentaryzować moduły, pola, workery lub formularze/zakładki (pola, sekcje, kolejność wprowadzania) z bibliotek DLL; (6) chce rozpocząć nowy dodatek/rozszerzenie Soneta — wygenerować szkielet źródeł z CLI (`dotnet new soneta-addon`, Soneta.MsBuild.SDK, szablony Soneta Platform Developer). Sięgnij też, gdy inny skill potrzebuje warstwy ORM/kodu biznesowego Soneta.4---56# Soneta Programming Basics - Podstawowe klasy ORM78Skill zawiera dokumentację fundamentalnych klas logiki biznesowej platformy Soneta. Klasy te stanowią podstawę mapowania obiektowo-relacyjnego (ORM) i są niezbędne do tworzenia kodu i dodatków.910> **Code review / refaktoring:** po napisaniu nowego kodu biznesowego oraz po każdym refaktoringu, **zawsze** zweryfikuj go względem [references/safe-code.md](references/safe-code.md). Ten sam dokument służy jako lista kontrolna do review PR-ów.1112## Mapa skilla1314SKILL.md zawiera "duży obraz" - hierarchię klas, thread-safety, kanoniczne wzorce. Po szczegóły konkretnego tematu sięgaj do referencji:1516| Temat | Gdzie szukać |17|---------------------------------------------------------------------------------------------------|---|18| **Nowy dodatek od zera** — wygenerowanie szkieletu źródeł przez CLI (`dotnet new soneta-addon`, Soneta.MsBuild.SDK, `global.json`/`Directory.Build.props`, solucja, debug VS Code) | [references/new-addon-cli.md](references/new-addon-cli.md) |19| Hierarchia ORM, Row / Table / Module, klucze, ISessionable | sekcje poniżej |20| Implementacja klas Row/Table, konstruktory (pola readonly, `RowCreator`), selector + `[BusinessRow]`, `[NewRow]`, `[DefaultConstructor]`, jawne wartości enum'ów, **zdarzenia cyklu życia** (`OnAdded`/`OnLoaded`/`OnEditing`/`OnDeleting`/`OnDeleted`/`OnRepacked` — override z `base.OnXxx()`; alternatywa bez dziedziczenia: statyczne delegaty `XSchema`) | [references/row-types.md](references/row-types.md) |21| `AssemblyAttributes` - odczyt atrybutów z załadowanych modułów (`GetCustom<T>`, `Find`, iteracja po assembly, cache, analiza DLL w runtime) | [references/assembly-attributes.md](references/assembly-attributes.md) |22| Sesje, transakcje, Login, Database, BusApplication, optimistic locking | [references/session-login.md](references/session-login.md) |23| Paczki danych, Datapack, GuidedRow, ExportedRow, synchronizacja, blokady | [references/datapack-guidedrow.md](references/datapack-guidedrow.md) |24| Klasa Context - dane z UI, zaznaczenia, parametry workera | [references/context.md](references/context.md) |25| Klasy parametrów (ContextBase) - filtry, trwałość, InvokeChanged (ta sama konstrukcja służy jako **parametry wydruków** → `/soneta-repx`) | [references/contextbase.md](references/contextbase.md) |26| Obiekty Worker i Extender - rozszerzenia modelu, akcje w menu Czynności (`[Action]` i dynamiczne `GetActions`), **rozszerzanie obiektów obcych** (property/kolekcje dodatku na obcym typie Soneta bez modyfikacji jego kodu) | [references/worker-extender.md](references/worker-extender.md) |27| Serwisy biznesowe (App / Database / Login / Session scope), **rejestracja warunkowa ServiceInitializer** (per instancja właściciela, np. per silnik bazy; kolejność po Priority) | [references/services.md](references/services.md) |28| Tłumaczenia (Translate, TranslateIgnore), ILogger, ActSource, **lokalizacja plików logów** (katalog `Soneta/Logs/` — macOS: `~/Library/Application Support/`, Windows: `%APPDATA%\`; pliki `server-*`/`dbmgr-*`/`test-*`) | [references/translations-logging.md](references/translations-logging.md) |29| Action result (rezultaty workera) zwracany przez worker / extender / Command - raporty, dialogi, nawigacja | [references/action-result.md](references/action-result.md) |30| RowCondition - serwerowe warunki LINQ, filtrowanie SubTable / View / Query | [references/rowcondition.md](references/rowcondition.md) |31| ViewInfo - definicja widoków list (folderów i inline jako property), CreateView, args.DataSource, klasa Params, `[Accessor(AutoChange)]`, powiązanie z viewform.xml | [references/viewinfo.md](references/viewinfo.md) |32| ChangeInfos - dziennik zmian / audyt (`session.ChangeInfos.Add`, pola Info/Data, pułapka 255 znaków, ChangeInfoType, prezentacja listy) | [references/changeinfos.md](references/changeinfos.md) |33| Cechy (Features) - tabela Features, typy cech, dostęp typowany/nietypowany, bindowanie w form.xml | [references/features.md](references/features.md) |34| Weryfikatory - walidacja spójności danych (`Verifier`, `RowVerifier<T>`, `ColVerifier<T>`, `MultiColVerifier<T>`, `RequiredVerifier`), poziomy `Error`/`Warning`/`Information`, uzbrajanie na zmianę pól-źródeł (`Session.Verifiers.Add`, `<verifier>` w business.xml), blokada `Save()` przy błędzie | [references/verifiers.md](references/verifiers.md) |35| Eventy (zdarzenia sesji) - odraczanie ciężkich obliczeń i logika w transakcji serwerowej: **sesyjne** (`Session.Events`, odpalane na `CommitUI`/`Save`/`Invoke`) vs **serwerowe** (`Session.ServerEvents`, w transakcji `Save` — numeracja). Rejestracja `Add(handler[, args])`, dedup po `Equals`/`GetHashCode`, wymuszanie `Invoke(handler)`, argumenty (`BusEventArgs`, `RowEventArgs<TRow>`, `SessionBusEventArgs`), priorytety, transakcyjność | [references/events.md](references/events.md) |36| Źródła praw (`IRightsSource`) - obiekt sterujący dostępem do danych operacyjnych, `AccessRight`, `Login.GetObjectRight`; nowe źródło domyślnie Denied, `AccessDeniedException` przy odczycie kolumn bez prawa, nadawanie praw i cache ról. **Miejsce tabeli w drzewie uprawnień** (pliki `*.rightstree.xml`, reguła korzenia praw, relacje `relright`/`relguided`) → skill `/soneta-business-xml` | [references/rights-source.md](references/rights-source.md) |37| Notacja klamrowa (`AccessorFormatter`) - wstawki `{ścieżka}` / `{ścieżka:format}` w captionach, szablonach, promptach; metadane modeli (`ApplicationInfo`, `TableInfo`) | [references/metadata-formatting.md](references/metadata-formatting.md) |38| Gotowe wzorce kodu end-to-end (import, CRUD, obsługa błędów) | [references/examples.md](references/examples.md) |39| Receptury kodu per obiekt biznesowy (domena CRM) — `Kontrahent` (pola, kolekcje, workery, finanse, RODO, KSeF). Indeks + mapa receptur (CRM-W1–W18); rozdziały `references/domeny/crm/CRM01..CRM10` | [references/domeny/crm.md](references/domeny/crm.md) |40| Receptury kodu per obiekt biznesowy (domena Handel) — `DokumentHandlowy` (faktury/magazynowe/zamówienia/korekty, relacje `IRelacjeService`, cykl życia, magazyn/partie/obroty, VAT/waluty, płatności, KSeF/fiskal/Intrastat, wydruki). Indeks + mapa receptur (HANDEL-W1–W82); rozdziały `references/domeny/handel/HANDEL01..HANDEL14` | [references/domeny/handel.md](references/domeny/handel.md) |41| Receptury kodu per obiekt biznesowy (domena Kadry-Płace) — `Pracownik` (zatrudnienie i dane kadrowe, historia `PracHistoria`+`Etat`, dodatki, nieobecności/limity, plan pracy/RCP, umowy cywilnoprawne, naliczanie wypłat, listy płac, wydruki PDF). Indeks + mapa receptur (KADRY-A*…K*); rozdziały `references/domeny/kadry/KADRY01..KADRY11` | [references/domeny/kadry.md](references/domeny/kadry.md) |42| **Zasady bezpiecznego kodu biznesowego — checklist do review i refaktoringu** | [references/safe-code.md](references/safe-code.md) |43| Pola obiektów biznesowych — dane wygenerowane (`data/props/`) + skaner/regeneracja z DLL | [references/scan-props.md](references/scan-props.md) |44| Moduły i tabele (`*Module` / `*Row` / `*Table`) — gotowy przegląd w `data/props/` (`INDEX.md` → `<Moduł>/INDEX.md`) + skaner z DLL | [references/scan-modules.md](references/scan-modules.md) |45| Inwentaryzacja workerów i extenderów (`[Worker<…>]`) z DLL | [references/scan-workers.md](references/scan-workers.md) |46| Zakładki, sekcje danych i pola formularzy (zasoby `*.pageform.xml`/`*.form.xml`) z DLL — katalog `data/forms/` (`INDEX.md` → `<Przestrzeń>.md`) + skaner na żądanie (`DataContext`/`EditValue`, `Include`, listy, kolejność pól pod kod i import XML) | [references/scan-forms.md](references/scan-forms.md) |47| Inwentaryzacja folderów statycznych menu (`[assembly: FolderView]`) z DLL — drzewo, listy, formularze | narzędzie `scan-folders` w skillu `/soneta-config` |48| Wydruki DevExpress (pliki `.repx`) — struktura raportu, źródło danych `BusinessDataSource`, rejestracja `[assembly: DxReport(...)]`; **kod-behind wydruku** (`ReportSnippet`/`[DxBind]`, lub generyczny `Snippet`/`[Bind]` bez licencji DevExpress) liczący dane raportu w oparciu o ORM. **Parametry wydruku konstruuje się jak parametry workerów — klasa `ContextBase` + `[Context]`** (patrz wyżej) | skill `/soneta-repx` (warstwa `.repx`) |49| **Testowanie na żywej aplikacji przez `buscall call` (CLI)** — zdalne sterowanie programem (nawigacja, formularze, gridy, edycja) i **zrzuty ekranu** do analizy wizualnej; jednorazowe wywołania CLI bez zarządzania procesem (`buscall --db <Baza> call <metoda> klucz=wartość`), plus wariant MCP `callmcp` | [references/buscall-live-testing.md](references/buscall-live-testing.md) (weryfikacja na żywo) |50| **Import/eksport XML z kodu — `SessionReader`/`SessionWriter`** — konstruktory (`Session` vs `Login`), obsługa błędów (`CollectExceptions`, zdarzenie `ReaderException`/`ReaderResponse`), mapowanie GUID-ów (`AddGuidMap`, `GuidMapPolicy`), tryby kolekcji (`RelationsImportMode`), eksport datapacku z subexports, `ImportBusinessXml` w testach; struktura samego pliku XML → artykuł *import-export-xml* w `/soneta-config` | [references/sessionreader-sessionwriter.md](references/sessionreader-sessionwriter.md) |51| **Testy integracyjne — klasa bazowa `TestBase`** — testy na realnej bazie z automatycznym rollbackiem (dwupoziomowa transakcja), wybór bazy `[TestDatabase]` (`nunit_default`/`nunit_ui`/`nunit_premiumui`), cykl życia (`ClassSetup`/`TestSetup`), `Session`/`ConfigSession`/`Context`, `InTransaction`/`SaveDispose`, podmiana DI (`ConfigureLoginServices`), asercje `AwesomeAssertions`, testy SQL (`SqlTraceInfo`), konwencje: nazewnictwo `Should_..._When_...` + prefiks grupy, `[Description]`, struktura AAA | [references/integration-tests.md](references/integration-tests.md) |5253## Nowy dodatek od zera (CLI)5455Gdy zaczynasz **nowy** dodatek (a nie modyfikujesz istniejący), najpierw wygeneruj szkielet źródeł56z wiersza poleceń, a dopiero potem wypełniaj go treścią. Robi to paczka szablonów **Soneta Platform57Developer** w oparciu o **Soneta.MsBuild.SDK** — jednym poleceniem powstają rozdzielone projekty58(logika biznesowa, UI, testy) już skonfigurowane pod platformę Soneta:5960```bash61dotnet new install Soneta.Platform.Developer # raz na maszynę (starsza składnia: dotnet new -i …)62dotnet new soneta-addon -n MyExtension -o ./MyExtension63# >>> obowiązkowy krok konfiguracji (patrz niżej) — nadpisz 3 pliki poprawną treścią <<<64cd ./MyExtension && dotnet build # weryfikacja65```6667Powstaje gotowa solucja `MyExtension.sln` z projektami `MyExtension` (logika), `MyExtension.UI`,68`MyExtension.Tests` oraz pliki `global.json` (wersja SDK) i `Directory.Build.props` (wersja bibliotek69`SonetaPackageVersion`). SDK sam pobiera biblioteki Soneta i uruchamia generator70`*.business.xml → *.business.cs` przy buildzie. Dodatki celują w **`net10.0`** i są **cross-platform**71— pełny cykl (generowanie, build, testy) działa na macOS/Linux/Windows.7273**Obowiązkowy krok po wygenerowaniu — nadpisz pliki konfiguracyjne poprawną treścią.** Szablon74`1.0.7` generuje konfigurację, która **nie buduje się z pudełka**, więc zaraz po `dotnet new` (a przed75`dotnet build`) doprowadź trzy pliki do poprawnej postaci — nie czekaj, aż build padnie:76771. **`global.json`** — przypnij `"Soneta.Sdk": "1.2.0"` (szablon wpisuje starszą, np. `1.1.8`).782. **`Directory.Build.props`** — dodaj w pierwszym `PropertyGroup`79 `<SonetaTargetFramework>net10.0</SonetaTargetFramework>` (szablon zostawia tę zmienną pustą).803. **`NuGet.Config`** — utwórz w katalogu solucji z feedem Soneta81 (`https://nuget.soneta.pl/v3/index.json`); szablon **nie generuje** tego pliku.8283Gotowa treść wszystkich trzech plików: [references/new-addon-cli.md](references/new-addon-cli.md#5a-pliki-konfiguracyjne--poprawna-treść-krok-obowiązkowy).84Dopiero po tym uruchom `dotnet build` (powinien przejść z 0 ostrzeżeń, 0 błędów).8586**`dotnet build` = walidator obu warstw** (generacja `business.xml → business.cs` **i** C#) — jednym87poleceniem wychodzą błędy generatora i braki klas konkretnych; to najszybsza pętla iteracji.88W raportach rozróżniaj „zweryfikowane buildem" vs „wymaga weryfikacji runtime" (żywa aplikacja /89testy) — zielony build ≠ działające runtime.9091**Kryterium ukończenia:** w katalogu docelowym istnieją trzy `*.csproj`, `global.json`92i `Directory.Build.props`. Dalej wypełniasz szkielet: definicje (**soneta-business-xml**) → kod93biznesowy (ten skill) → UI (**soneta-form-xml**).9495Pełna procedura — szablony, pliki konfiguracyjne, parametry MSBuild, solucja, debug w VS Code,96referencje WinForms, ręczny fallback bez szablonów i troubleshooting:97[references/new-addon-cli.md](references/new-addon-cli.md).9899## Architektura warstw100101```102BusApplication.Instance (singleton) - multithreaded103 └── Database104 └── Login105 └── Session - single-threaded106 └── Module107 └── Table108 └── Row109```110111## 3 poziomy logiki biznesowej112113| Poziom | Opis | Przykłady klas |114|--------|------|----------------|115| **1. Bazowe** | Klasy wspólne dla wszystkich modułów (Soneta.Business.dll) | `Row`, `Table`, `Module`, `Session`, `Context` |116| **2. Generowane** | Klasy generowane przez BusinessGenerator z `*.business.xml` (sufiksy: Row, Table, Module) | `TowarRow`, `TowarTable`, `TowaryModule` |117| **3. Implementowane** | Klasy konkretne tworzone przez programistę | `Towar`, `Towary` (bez sufiksów) |118119**BusinessGenerator** jest automatycznie uruchamiany podczas kompilacji dla plików `*.business.xml`. Szczegółowy opis definiowania business.xml znajduje się w skill **soneta-business-xml**.120121Klasy poziomu 3 (`Towar`, `Towary`) pisze programista, dziedzicząc po klasach generowanych.122Gdy tabela ma pola `readonly` (w tym **selector**), klasa obiektu biznesowego wymaga123dodatkowych konstruktorów; selector pozwala też przechowywać **wiele typów obiektów w jednej124tabeli**. Wzorce implementacji (konstruktory, `[BusinessRow]`, `[NewRow]`) —125[references/row-types.md](references/row-types.md).126127## Hierarchia głównych klas128129```130Row (abstrakcyjna)131 └── GuidedRow (+ Guid, Attachments, ChangeInfos)132 └── ExportedRow (+ Exported flag - rozróżnia dane konfiguracyjne od operacyjnych)133134Table (abstrakcyjna)135 └── GuidedTable (indeksator po Guid)136 └── ExportedTable137138Module (abstrakcyjna)139 └── [NazwaModulu]Module (np. TowaryModule)140```141142Szczegóły GuidedRow / ExportedRow (flaga `Exported`, atrybuty `guided` / `relguided` w business.xml, ChangeInfos, blokady) - patrz [references/datapack-guidedrow.md](references/datapack-guidedrow.md).143144## Thread-safety145146### Obiekty single-threaded (NIE współdzielić między wątkami)147- `Session`148- `Module`149- `Table`150- `Row`151- `Context`152- oraz wszystkie klasy pochodne153154### Obiekty multi-threaded (można współdzielić)155- `BusApplication`156- `Database`157- `Login`158159## Klasa Session - fundamenty160161Session to kluczowa klasa do zarządzania danymi. **Każda operacja na danych wymaga sesji.** Sesja jest single-threaded i implementuje `IDisposable` - zawsze opakowuj w `using`.162163```csharp164Session session = login.CreateSession(readOnly: false, config: false, name: "MojaSesja");165```166167**Każda modyfikacja obiektu MUSI być w transakcji biznesowej** otwieranej przez `Session.Logout(editMode: true)` - dotyczy dodawania, modyfikacji właściwości oraz kasowania:168169```csharp170using (var transaction = session.Logout(editMode: true))171{172 towar.Nazwa = "Zmieniona nazwa";173 transaction.Commit(); // Commit() w kodzie biznesowym174 // transaction.CommitUI(); // CommitUI() w kodzie UI (worker, extender, Command)175}176session.Save(); // zapis do bazy - optimistic-lock; konflikty wykrywane tu177```178179**Brak Commit() = automatyczny rollback przy Dispose()** - dotyczy też transakcji tylko do odczytu.180181Pełna dokumentacja (typy sesji edycyjna / readonly / konfiguracyjna, transakcje zagnieżdżone, Commit vs CommitUI, optimistic locking, mieszanie obiektów z różnych sesji przez `session.Get(obiekt)`) - patrz [references/session-login.md](references/session-login.md).182183## Klasa Module184185Moduł grupuje logicznie powiązane tabele. **Nie ma odwzorowania w bazie danych.**186187**Preferowany odczyt modułu to metoda rozszerzająca `session.GetX()` (generowanymi z `business.xml`, także dla modułów dodatków). ** Wariant188`Module.GetInstance(ISessionable)` stosuj tylko, gdy nie masz dostępu do property `Session`.189Nie używaj ogólnego `session.Get<TModule>()` ani `session.Modules[...]` — zasada190[§14.4 w safe-code.md](references/safe-code.md).191192```csharp193// Dostęp do modułu - extension method (preferowane)194var tm = session.GetTowary();195var hm = session.GetHandel();196var crm = session.GetCRM();197var bm = session.GetBusiness();198199// Dostęp do tabel200Towary towary = tm.Towary;201Jednostki jednostki = tm.Jednostki;202203// Moduł implementuje ISessionable204Session s = tm.Session;205```206207## Klasa Table208209Reprezentuje tabelę w bazie danych. Udostępnia dostęp do kolekcji wierszy.210211```csharp212var towary = tm.Towary;213214// Iteracja po kluczu podstawowym215foreach (Towar t in towary.WgKodu) { ... }216217// Iteracja po innym kluczu218foreach (Towar t in towary.WgNazwy) { ... }219220// Właściwości221Table.AccessRight // Prawa dostępu222Table.Session // Sesja (ISessionable)223Table.Module // Moduł nadrzędny224Table.PrimaryKey // Klucz podstawowy225```226227## Klasa Row228229Reprezentuje pojedynczy wiersz (rekord) z tabeli.230231### Właściwości bazowe232233```csharp234Row.ID // int - PODSTAWOWY identyfikator obiektu w tabeli (autoincrement, Primary Key)235Row.State // RowState - stan obiektu w sesji236Row.Table // Table - tabela nadrzędna237Row.Module // Module - moduł238Row.Session // Session - sesja239```240241**ID** jest automatycznie generowany przez bazę danych i stanowi klucz główny (Primary Key) tabeli.242243### Stany obiektu (RowState)244245| Stan | Opis |246|------|------|247| `Detached` | Nowy obiekt, nie przypisany do tabeli |248| `Unchanged` | Wczytany z bazy, bez zmian |249| `Modified` | Zmodyfikowany w sesji |250| `Added` | Nowy, dodany do tabeli, nie zapisany w bazie |251| `Deleted` | Skasowany, do usunięcia z bazy |252253## Klucze i indeksy254255Definiowane w *.business.xml, mapowane na indeksy w bazie danych.256257```xml258<key name="WgKodu" keyunique="true" keyprimary="true">259 <keycol name="Kod"/>260</key>261```262263| Atrybut | Znaczenie |264|---------|-----------|265| `keyprimary="true"` | Klucz podstawowy (domyślne sortowanie) |266| `keyunique="true"` | Wartości unikalne w tabeli |267268**Uwaga:** `keyprimary` w business.xml to **nie to samo** co Primary Key w bazie (który jest zawsze na kolumnie ID).269270## Interfejs ISessionable271272```csharp273public interface ISessionable {274 Session Session { get; }275}276```277278Implementują go: `Session`, `Module`, `Table`, `Row`, `Context`, `Key`.279280Używany jako argument funkcji wymagających kontekstu sesji:281282```csharp283// Metoda statyczna GetInstance - akceptuje ISessionable284public static TowaryModule GetInstance(ISessionable session)285286// Extension method - wygodniejsza składnia (tylko dla Session)287public static TowaryModule GetTowary(this Session session)288```289290**Zalecane użycie:**291```csharp292// Extension method (prostsze)293var tm = session.GetTowary();294var tm = context.Session.GetTowary();295var tm = towar.Session.GetTowary();296297// GetInstance (gdy mamy ISessionable, ale property Session jest niedostępne)298var tm = TowaryModule.GetInstance(sessionable);299```300301## Kod biznesowy vs UI302303Kod biznesowy realizuje operacje logiki biznesowej (jak backend). Kod UI (frontend) jest odpowiedzialny za prezentację danych i interakcję z użytkownikiem. Kod biznesowy może być umieszczony w tej samej klasie z kodem UI.304305Kod UI to np.:306- obiekty `View`, `ViewInfo`, extender307- metody sterujące `IsReadOnlyXxx`, `IsVisibleXxxx`, `GetListXxx`, `IsEnabledXxx`, `GetNameXxx`, `GetAppearanceXxx`308309### Ważne zasady do stosowania w kodzie biznesowym310311- Nie używaj żadnych obiektów kodu UI, w szczególności `View` - zamiast tego możesz użyć `SubTable[condition]`.312- Nie należy stosować warunków na prawa dostępu (np. `if (Table.AccessRight == AccessRights.Denied) {...}`).313314Szczegóły konstruowania ViewInfo (atrybut `FolderView`, eventy `CreateView`/`InitContext`, klasa `Params`, powiązanie z `viewform.xml`) opisuje [references/viewinfo.md](references/viewinfo.md).315316## Szybki start - wzorce kodu317318Kanoniczny wzorzec transakcji biznesowej (`Logout → zmiana → Commit → Save`) pokazuje319sekcja [Klasa Session - fundamenty](#klasa-session---fundamenty) powyżej. Gotowe wzorce320end-to-end (odczyt, tworzenie, modyfikacja, kasowanie, obsługa błędów, pełny import) -321patrz [references/examples.md](references/examples.md).322323Gotowe **receptury per obiekt biznesowy** (realne pola, kolekcje i workery) są w `references/domeny/`:324[crm.md](references/domeny/crm.md) (`Kontrahent`), [handel.md](references/domeny/handel.md)325(`DokumentHandlowy`), [kadry.md](references/domeny/kadry.md) (`Pracownik`),326[workflow.md](references/domeny/workflow.md) (procesy i zadania).327328## Narzędzia pomocnicze329330Skill udostępnia skrypty `dotnet script` (`scripts/`) do statycznej inwentaryzacji bibliotek Soneta — bez ładowania IL do CLR (metadane typów: Roslyn `MetadataReference.CreateFromFile`; zasoby osadzone: `System.Reflection.Metadata`):331332- **Pola tabel — najpierw dane wygenerowane.** Kontrakty pól **wszystkich ~1200 tabel** są już wyeksportowane do [`data/props/`](data/props/) (plik `<Moduł>/<RowType>.md` na tabelę). Odczyt jest natychmiastowy — nie skanuj DLL, gdy tabela tam jest. Znasz `RowType`? Nie czytaj indeksów, tylko `ls data/props/*/DokumentHandlowy.md` (pełny zestaw poleceń niżej). Jak regenerować po zmianie wersji: [references/scan-props.md](references/scan-props.md).333- **Moduły i tabele — indeks dwupoziomowy.** [`data/props/INDEX.md`](data/props/INDEX.md) to **router**: statystyki, polecenia wyszukiwania i lista 37 modułów. Inwentaryzacja tabel modułu jest w `data/props/<Moduł>/INDEX.md` (`RowType | Tytuł | Tabela | Konfig | Guided | Historia | Interfaces | Selektor | Plik`; kolumna `Historia`: `historyczna → H` / `historia → P`). Podział jest celowy — monolityczny indeks (212 KB) przekraczał limit jednego odczytu.334- **Interfejsy — [`data/props/Interfaces.md`](data/props/Interfaces.md).** Wszystkie interfejsy z `[TableInfo(Interfaces=...)]` i tabele je implementujące (relacje interfejsowe w jednym miejscu, tabele linkowane do plików).335- `scan-modules.csx` — skaner **na żądanie** listujący moduły i tabele z DLL (dla innego katalogu DLL niż ten, z którego zbudowano `data/props/`). Szczegóły: [references/scan-modules.md](references/scan-modules.md).336- `export-props-all.csx` — **wsadowo** regeneruje cały `data/props/` z DLL (kompilacja budowana raz, ~1200 tabel w kilka sekund). Uruchamiaj po zmianie wersji platformy lub przebudowie dodatku. Szczegóły: [references/scan-props.md](references/scan-props.md).337- `scan-props.csx` — skaner **na żądanie** dla pojedynczej tabeli (fallback, gdy tabeli nie ma w `data/props/`). Wypisuje pola i właściwości kalkulowane klasy biznesowej, rekurencyjnie po polach typu subrow, oraz — dla tabel z **selektorem** — sekcję podtypów (`[BusinessRow]`/`[NewRow]`). Szczegóły: [references/scan-props.md](references/scan-props.md).338- `scan-workers.csx` — wypisuje na stdout **JSON** z workerami i extenderami zarejestrowanymi atrybutem assembly `[Worker<…>]`, pogrupowanymi wg `DataType`. Dla każdej klasy: parametry inicjowane z `Context` (ctor + `[Context]`, z rozwinięciem pod-property dla typów dziedziczących z `ContextBase`), property do bindowania, akcje menu Czynności. Opcjonalny drugi argument filtruje wynik do konkretnego typu danych (np. `DokumentHandlowy`) — w praktyce konieczny, bo pełne skanowanie zwraca tysiące rejestracji. Wynik łatwo przetwarzać `jq`. Szczegóły: [references/scan-workers.md](references/scan-workers.md).339- **Formularze — najpierw INDEX.** Katalog **wszystkich ~5700 zakładek** (pageform) jest wyeksportowany do [`data/forms/`](data/forms/): [`INDEX.md`](data/forms/INDEX.md) to **router** (sposoby wyszukiwania, lista 26 przestrzeni nazw, sekcja systemowa), a zakładki biznesowe leżą w `data/forms/<Przestrzeń>.md` (`Typ danych | Zakładka (plik) | Nazwa zakładki | Priority | Biblioteka | Przestrzeń`, sortowane po typie). **Nie musisz znać przestrzeni** — `rg '^\| Kontrahent \|' data/forms/*.md` przeszukuje wszystkie naraz. Typ = segment przed nazwą zakładki w `…<TYP>.<ZAKŁADKA>.pageform.xml` (albo `DataType`). Zakładki typów ogólnych (`Row`/`IRow`/…) wydzielone jako **systemowe** (osobna sekcja routera; `scan-forms` nie raportuje ich per obiekt). Odczyt natychmiastowy, bez DLL. Regeneracja: `export-forms-index.csx`. Szczegóły: [references/scan-forms.md](references/scan-forms.md).340- `scan-forms.csx` — skaner **na żądanie** dla pojedynczego obiektu: odczytuje z zasobów osadzonych (`*.pageform.xml`/`*.form.xml`) **zakładki, sekcje danych (grupy) i pola** w **kolejności wprowadzania**, rozwijając ścieżki pól przez łańcuch `DataContext`/`EditValue`, dołączane `Include` (także między bibliotekami) oraz listy (`Grid`/`Scheduler`/… → kolumny elementu kolekcji, separator `:` między wczytaniem listy a polami elementu). Argument = prefiks nazwy pliku lub `DataType`/`Namespace.Typ`. Do budowania danych kodem i importu XML `business="true"`. Szczegóły: [references/scan-forms.md](references/scan-forms.md).341- `export-forms-index.csx` — **wsadowo** generuje `data/forms/INDEX.md` i pliki przestrzeni z DLL (jeden przebieg, ~5700 zakładek w ~1 s). Uruchamiaj po zmianie wersji platformy lub przebudowie dodatku.342343### Jak szukać w danych wygenerowanych344345Dane w `data/` są tak zorganizowane, że **nazwa pliku niesie informację** — najczęściej346wystarczy `ls` albo jeden `rg`, bez czytania indeksów. Polecenia uruchamiaj z katalogu skilla:347348```bash349# Pola tabeli, gdy znasz RowType — plik nazywa się <Moduł>/<RowType>.md350ls data/props/*/DokumentHandlowy.md # → data/props/Handel/DokumentHandlowy.md351ls data/props/*/*Pracownik*.md # gdy nie znasz dokładnej nazwy352353# Tabela po nazwie w bazie (kolumna `Tabela`) lub po tytule354rg -l '`DokHandlowe`' data/props/*/INDEX.md355356# Które tabele mają pole wskazujące na dany obiekt (relacje)357rg -l 'Soneta\.CRM\.Kontrahenci\.Kontrahent`' data/props/*/*.md358359# Zakładki obiektu — bez znajomości przestrzeni nazw360rg '^\| Kontrahent \|' data/forms/*.md361362# Które tabele implementują interfejs363rg '^\| `IDokument' data/props/Interfaces.md364```365366**Nie czytaj `data/props/<Moduł>/INDEX.md` ani `data/forms/<Przestrzeń>.md` w całości**, gdy367szukasz jednego obiektu — to setki wierszy. Indeksy służą do przeglądu modułu, `rg`/`ls` do368trafienia w konkret.369370Inwentaryzację **folderów statycznych menu** (`[assembly: FolderView]`) — drzewo pozycji menu, list i formularzy — realizuje narzędzie **`scan-folders`** przeniesione do skilla **`/soneta-config`** (komplementarne do `scan-modules`: perspektywa funkcjonalno-użytkowa zamiast danych).371372## Konwencje nazewnicze373374| Element | Konwencja | Przykład |375|---------|-----------|----------|376| Klasa wiersza (Row) | PascalCase, l.poj. | `Towar`, `Kontrahent` |377| Klasa tabeli | PascalCase, l.mn. | `Towary`, `Kontrahenci` |378| Klasa modułu | Nazwa + Module | `TowaryModule` |379| Klucz | Wg + nazwa kolumny | `WgKodu`, `WgNazwy` |380| Namespace | Soneta.NazwaModułu | `Soneta.Towary` |381382### Język identyfikatorów383384| Typ | Język | Przykłady |385|-----|-------|-----------|386| Logika biznesowa | **polski** | `Towar`, `Kontrahent`, `DokumentHandlowy`, `Faktura` |387| Identyfikatory systemowe | **angielski** | `Session`, `Context`, `Row`, `Table`, `Module` |388389**Można łączyć polski i angielski** w nazwach metod i klas:390391```csharp392RetrieveTowary()393UpdateKontrahent()394GetDokumentyHandlowe()395CreateFaktura()396```