Przemianowałem istnijące skille na prostsze nazwy - porządki

This commit is contained in:
Marcin Wojas
2026-05-16 16:06:36 +02:00
parent e1c3be3846
commit 3e5239cb60
10 changed files with 277 additions and 91 deletions
+25 -19
View File
@@ -1,10 +1,16 @@
# Soneta / enova365 AI Skills # Soneta / enova365 AI Skills
Zestaw skills dla asystentów AI (Claude, Cursor, Windsurf, itp.) wspierających programowanie i projektowanie z platformą **enova365/Soneta Enterprise**. Zestaw skills dla asystentów AI (Claude, Cursor, Windsurf, itp.) wspierających programowanie, projektowanie i konfigurację platformy **enova365 / Soneta Enterprise / Triva**.
## Dostępne skille ## Dostępne skille
### 1. soneta-programming-basics ### 0. soneta-erp (meta-skill)
Mapa i przewodnik po pozostałych skillach. Pomaga wybrać właściwy skill w zależności od warstwy zadania (dane, UI, logika, płace).
**Kiedy używać:** rozpoczynasz nowe zadanie dla enova365/Soneta/Triva i nie wiesz, który skill zastosować; zadanie obejmuje wiele warstw platformy i potrzebna jest koordynacja między skillami.
### 1. soneta-programming
Fundamentalne klasy ORM platformy enova365/Soneta Enterprise. Fundamentalne klasy ORM platformy enova365/Soneta Enterprise.
@@ -68,36 +74,36 @@ System projektowy (design system) Soneta / enova365 do budowania aplikacji webow
**Kiedy używać:** projektowanie stron/aplikacji w stylu enova365, dashboardy, formularze, strony logowania, panele administracyjne. **Kiedy używać:** projektowanie stron/aplikacji w stylu enova365, dashboardy, formularze, strony logowania, panele administracyjne.
### 6. soneta-mcp-ui-guide ### 6. soneta-place-def-elementow
Obsługa programów Soneta (enova365, Triva) przez narzędzia MCP `soneta_ui`. Tworzenie i konfiguracja definicji elementów wynagrodzenia w enova365 (moduł Płace).
**Zakres:** **Zakres:**
- Nawigacja po modułach i folderach programu (Handel, Kadry, Księgowość, CRM, itp.) - Algorytmy naliczania: kreator, edytor C# (`_Param`, `_Wylicz`, `_Wartość1h`), algorytmy wbudowane
- Przeglądanie list z filtrowaniem i stronicowaniem - 12 wzorców dla Dodatków, 5 dla Nieobecności, 5 dla Dodatków automatycznych (z analizy ~247 definicji)
- Otwieranie formularzy i przełączanie zakładek - Receptury kodu C#: iterowanie po elementach, staż pracy, wymiar etatu, czas pracy, wskaźniki, cechy pracownika
- Edycja danych na formularzach (pola oznaczone jako `edytowany`) - Konfiguracja zakładek: Ogólne, Deklaracje (PIT/ZUS), Nieobecności, Algorytm
- Dodawanie nowych obiektów - Metody sterujące naliczaniem (`_PodstawaUrlopu`, `_PodstawaZasiłku`)
- Mapa ~100 najczęściej używanych folderów programu
**Kiedy używać:** odczyt/edycja danych w enova365 lub Triva przez MCP — kontrahenci, faktury, pracownicy, towary, stany magazynowe, przelewy, deklaracje. **Kiedy używać:** tworzenie/modyfikacja definicji elementu wynagrodzenia, pisanie algorytmów płacowych (premia procentowa, dodatek stażowy, zasiłek chorobowy, ekwiwalent za urlop).
## Powiązania między skillami ## Powiązania między skillami
Skille są zaprojektowane do współpracy: Skille są zaprojektowane do współpracy:
1. **soneta-addon-planning** → planuje strukturę nowego dodatku 1. **soneta-erp** → wskazuje właściwy skill dla danego zadania
2. **soneta-business-xml** → definiuje obiekty biznesowe w XML 2. **soneta-addon-planning** → planuje strukturę nowego dodatku
3. **soneta-programming-basics** → pokazuje jak pracować z wygenerowanymi klasami C# 3. **soneta-business-xml** → definiuje obiekty biznesowe w XML
4. **soneta-form-xml** → tworzy formularze UI dla obiektów 4. **soneta-programming** → pokazuje jak pracować z wygenerowanymi klasami C#
5. **soneta-ui-style** → styluje interfejs webowy zgodnie z design systemem enova365 5. **soneta-form-xml** → tworzy formularze UI dla obiektów
6. **soneta-mcp-ui-guide** → obsługuje dane w działającej instancji enova365/Triva przez MCP 6. **soneta-ui-style** → styluje interfejs webowy zgodnie z design systemem enova365
7. **soneta-place-def-elementow** → konfiguruje warstwę płacową (definicje elementów wynagrodzenia)
## Instalacja ## Instalacja
### Claude Code ### Claude Code
Skopiuj folder ze skillem do `~/.claude/skills/`. Skopiuj foldery skilli do `~/.claude/skills/`.
### Cursor / Windsurf / inne IDE ### Cursor / Windsurf / inne IDE
@@ -105,4 +111,4 @@ Dodaj zawartość skilli do kontekstu projektu lub rules.
## Licencja ## Licencja
MIT MIT
@@ -87,7 +87,7 @@ Plan projektu generowany jest jako dokument Markdown z następującą strukturą
Po zatwierdzeniu planu projektu: Po zatwierdzeniu planu projektu:
1. **enova365-business-xml** - generowanie pliku business.xml na podstawie modelu danych 1. **enova365-business-xml** - generowanie pliku business.xml na podstawie modelu danych
2. **soneta-programming-basics** - implementacja workerów i logiki biznesowej 2. **soneta-programming** - implementacja workerów i logiki biznesowej
## Szczegółowa dokumentacja ## Szczegółowa dokumentacja
+1 -1
View File
@@ -325,4 +325,4 @@ Po zakończeniu wszystkich etapów wygeneruj dokument TODO z kolejnymi krokami:
Po zatwierdzeniu planu projektu: Po zatwierdzeniu planu projektu:
1. **soneta-business-xml** — generowanie pliku business.xml na podstawie modelu danych z Etapu 3 1. **soneta-business-xml** — generowanie pliku business.xml na podstawie modelu danych z Etapu 3
2. **soneta-form-xml** — generowanie formularzy i widoków UI na podstawie sekcji 3.4 i 3.5 2. **soneta-form-xml** — generowanie formularzy i widoków UI na podstawie sekcji 3.4 i 3.5
3. **soneta-programming-basics** — implementacja logiki biznesowej, workerów 3. **soneta-programming** — implementacja logiki biznesowej, workerów
+37
View File
@@ -0,0 +1,37 @@
---
name: soneta-erp
description: >
Mapa i przewodnik po wyspecjalizowanych skillach do pracy z platformą Soneta ERP
(enova365, Soneta Enterprise, Triva). Pomaga wybrać właściwy skill w zależności od
zadania: programowanie ORM (soneta-programming), planowanie dodatków
(soneta-addon-planning), definicje obiektów biznesowych (soneta-business-xml),
formularze UI (soneta-form-xml), definicje elementów wynagrodzenia
(soneta-place-def-elementow). Używaj tego skilla ZAWSZE gdy użytkownik: (1) rozpoczyna
nowe zadanie związane z enova365/Soneta/Triva i nie jest jasne, który wyspecjalizowany
skill zastosować; (2) pyta ogólnie o tworzenie dodatków, modułów lub rozszerzeń dla
Soneta ERP; (3) wspomina o platformie enova, Soneta Enterprise, Triva bez sprecyzowania
warstwy (dane, UI, logika, płace); (4) chce poznać dostępne narzędzia/skille do pracy
z ekosystemem Soneta; (5) realizuje zadanie obejmujące wiele warstw platformy
(np. nowy moduł z bazą danych, formularzami i logiką) i potrzebuje koordynacji między
skillami.
---
# Mapa skills podczas pracy z Soneta ERP (enova/Triva)
* `/soneta-programming` - Fundamentalne klasy ORM platformy enova365/Soneta Enterprise. Obejmuje mapowanie
obiektowo-relacyjne (Row, Table, Module), zarządzanie sesją (Session), logowanie (Login, Database, BusApplication),
paczki danych (Datapack, GuidedRow) oraz kontekst (Context). Używaj gdy użytkownik pyta o podstawowe klasy logiki
biznesowej, strukturę obiektów ORM, sesje i transakcje, hierarchię klas Row/Table/Module, mechanizm Datapack i
synchronizację danych, lub kontekst aplikacji enova365, Context
* `/soneta-addon-planning` - Planowanie projektów dodatków dla platformy enova365/Soneta Enterprise. Tworzy
kompletną dokumentację projektową obejmującą: strukturę danych (tabele, relacje),
elementy konfigurowalne, definicje list i menu, formularze, workery i raporty.
Używaj gdy użytkownik prosi o zaplanowanie nowego modułu/dodatku enova365,
przygotowanie założeń projektu, stworzenie specyfikacji funkcjonalnej dodatku,
lub zdefiniowanie struktury danych i interfejsu użytkownika dla nowego modułu.
* `/soneta-business-xml` - Generator plików business.xml dla platform Soneta (enova365, Soneta Enterprise).
Tworzy definicje obiektów biznesowych (tabel, kolumn, relacji, indeksów) zgodne
ze schematem XSD. Używaj gdy użytkownik prosi o stworzenie nowego modułu biznesowego,
zdefiniowanie obiektów lub encji do przechowywania w bazie danych, utworzenie relacji
między obiektami, lub generowanie plików business.xml dla enova365/Soneta Enterprise.
* `/soneta-form-xml` - XML z nieistniejącymi elementami. ZAWSZE używaj tego skilla gdy użytkownik: (1) prosi o utworzenie lub modyfikację pliku pageform.xml, viewform.xml, form.xml, lookupform.xml lub gridform.xml dla enova365/Soneta; (2) pyta o elementy DataForm, Page, Group, Grid, Field, Row, Stack, Flow, Command, Include, Appearance, GroupBy w enova365; (3) pyta o składnię EditValue, DataContext, Visibility, RowCondition, Renderable, CaptionHtml, Footer, Class lub układ UI formularzy enova365; (4) pokazuje istniejący plik form.xml/pageform.xml/viewform.xml i pyta o jego strukturę lub chce go rozszerzyć; (5) pyta o warunkową widoczność, formatowanie warunkowe (Appearance), bindowanie danych lub wzorce UI w Soneta/enova365.
+25 -10
View File
@@ -143,31 +143,46 @@ update_field_value(["_Tekst=public void Nazwa_Param(...) {\\n ...\\n}\\n\\npu
## Wskazówki przy tworzeniu nowych algorytmów ## Wskazówki przy tworzeniu nowych algorytmów
1. **Wybierz wzorzec** — większość nowych elementów pasuje do jednego z istniejących wzorców. Przeczytaj `references/wzorce-algorytmiczne.md` i zacznij od skopiowania najbliższego wzorca. 1. **`Element` to parametr metody** — w kodzie algorytmu `Element` nie jest zmienną globalną ani polem klasy — jest **pierwszym parametrem** każdej metody (`_Param`, `_Wylicz`, `_Wartość1h` itd.).
Metoda musi zawsze zawierać te parametry.
Typ tego parametru musi odpowiadać rodzajowi definiowanego elementu:
- Etat → `Soneta.Place.WypElementEtat Element`
- Dodatek → `Soneta.Place.WypElementDodatek Element`
- Dodatek automatyczny → `Soneta.Place.WypElementDodatekAutomatyczny Element`
- Nieobecność → `Soneta.Place.WypElementNieobecnosc Element`
- Nadgodziny → `Soneta.Place.WypElementNadgodziny Element`
- Umowa → `Soneta.Place.WypElementUmowa Element`
2. **Źródło kwoty** — zdecyduj skąd pochodzi podstawa: Przykład: jeśli algorytm odwołuje się do `Element.DodHistoria.Podstawa`, a definicja jest rodzaju **Dodatek**, to sygnatura metody musi wyglądać:
```csharp
public void MojDodatek_Param(WypElementDodatek Element, WypSkladnik Składnik) { ... }
```
2. **Wybierz wzorzec** — większość nowych elementów pasuje do jednego z istniejących wzorców. Przeczytaj `references/wzorce-algorytmiczne.md` i zacznij od skopiowania najbliższego wzorca.
3. **Źródło kwoty** — zdecyduj skąd pochodzi podstawa:
- Kwota z parametrów pracownika → `Element.DodHistoria.Podstawa` (Wzorzec A) - Kwota z parametrów pracownika → `Element.DodHistoria.Podstawa` (Wzorzec A)
- Kwota z konfiguracji programu → `module.Config.Zasiłki.*[Date]` (Wzorzec B) - Kwota z konfiguracji programu → `module.Config.Zasiłki.*[Date]` (Wzorzec B)
- Kwota stała z definicji → `Element.Definicja.Algorytm.KreatorAlgorytmu.Podstawa` (Wzorzec C) - Kwota stała z definicji → `Element.Definicja.Algorytm.KreatorAlgorytmu.Podstawa` (Wzorzec C)
- Procent od zasadniczego → `ZasadniczeNominalne(Date)` + `Element.DodHistoria.Procent` (Wzorzec D) - Procent od zasadniczego → `ZasadniczeNominalne(Date)` + `Element.DodHistoria.Procent` (Wzorzec D)
3. **Czas i dni** — prawie wszystkie algorytmy ustawiają czas i dni z normy: 4. **Czas i dni** — prawie wszystkie algorytmy ustawiają czas i dni z normy:
```csharp ```csharp
CzasDni cd = Element.Pracownik.Czasy.Norma(Składnik.Okres); CzasDni cd = Element.Pracownik.Czasy.Norma(Składnik.Okres);
Składnik.Czas = cd.Czas; Składnik.Czas = cd.Czas;
Składnik.Dni = cd.Dni; Składnik.Dni = cd.Dni;
``` ```
4. **Pomniejszenie za nieobecności** — użyj kreatora algorytmu z konfiguracją korekt lub ręcznie: `Element.Pracownik.Czasy.Nieobecnosci(Składnik.Okres).Dni` 5. **Pomniejszenie za nieobecności** — użyj kreatora algorytmu z konfiguracją korekt lub ręcznie: `Element.Pracownik.Czasy.Nieobecnosci(Składnik.Okres).Dni`
5. **Proporcjonalność do okresu** — gdy okres składnika jest krótszy niż pełny okres naliczania, przelicz proporcjonalnie (jak w Premii procentowej — Wzorzec D). 6. **Proporcjonalność do okresu** — gdy okres składnika jest krótszy niż pełny okres naliczania, przelicz proporcjonalnie (jak w Premii procentowej — Wzorzec D).
6. **Zasiłki** — zawsze używaj `new PodstawaZasiłku(Element)` i `WyliczPodstawęZasiłkuZaDzień(...)` — nie obliczaj podstawy zasiłku ręcznie. 7. **Zasiłki** — zawsze używaj `new PodstawaZasiłku(Element)` i `WyliczPodstawęZasiłkuZaDzień(...)` — nie obliczaj podstawy zasiłku ręcznie.
7. **Urlopy okolicznościowe** — używaj `new NaliczanieOkolicznosciowy(Element, Składnik).NaliczPodstawy()` — automatycznie ustawi Podstawa1 (za godziny) i Podstawa2 (za dni). 8. **Urlopy okolicznościowe** — używaj `new NaliczanieOkolicznosciowy(Element, Składnik).NaliczPodstawy()` — automatycznie ustawi Podstawa1 (za godziny) i Podstawa2 (za dni).
8. **Ekwiwalenty i odprawy** — używaj `new NaliczanieEkwiwalent(Element, Składnik).NaliczPodstawy()`. 9. **Ekwiwalenty i odprawy** — używaj `new NaliczanieEkwiwalent(Element, Składnik).NaliczPodstawy()`.
9. **Element.DodHistoria.Podstawa vs Element.DodHistoria.Kwota** — w rzeczywistych algorytmach kreatorowych kwota pobierana jest z `Element.DodHistoria.Podstawa` (nie `.Kwota`). Pole `.Kwota` występuje w dokumentacji, ale `.Podstawa` jest częściej stosowane w generowanym kodzie. 10. **Element.DodHistoria.Podstawa vs Element.DodHistoria.Kwota** — w rzeczywistych algorytmach kreatorowych kwota pobierana jest z `Element.DodHistoria.Podstawa` (nie `.Kwota`). Pole `.Kwota` występuje w dokumentacji, ale `.Podstawa` jest częściej stosowane w generowanym kodzie.
10. **Dodatkowe metody** — jeśli element ma odbiorców płatności (potrącenia, alimenty), zdefiniuj metody `_Odbiorca` i `_RachunekOdbiorcy`. Jeśli okres naliczania wymaga podziału, zdefiniuj `_CięcieOkresu`. Jeśli element wpływa na podstawy urlopów/zasiłków — przeczytaj `references/metody-sterujace-naliczaniem.md`. 11. **Dodatkowe metody** — jeśli element ma odbiorców płatności (potrącenia, alimenty), zdefiniuj metody `_Odbiorca` i `_RachunekOdbiorcy`. Jeśli okres naliczania wymaga podziału, zdefiniuj `_CięcieOkresu`. Jeśli element wpływa na podstawy urlopów/zasiłków — przeczytaj `references/metody-sterujace-naliczaniem.md`.
@@ -1,5 +1,5 @@
--- ---
name: soneta-programming-basics name: soneta-programming
description: > description: >
Fundamentalne klasy ORM platformy enova365/Soneta Enterprise. Obejmuje mapowanie Fundamentalne klasy ORM platformy enova365/Soneta Enterprise. Obejmuje mapowanie
obiektowo-relacyjne (Row, Table, Module), zarządzanie sesją (Session), logowanie obiektowo-relacyjne (Row, Table, Module), zarządzanie sesją (Session), logowanie
@@ -11,27 +11,19 @@ description: >
# Soneta Programming Basics - Podstawowe klasy ORM # Soneta Programming Basics - Podstawowe klasy ORM
Skill zawiera dokumentację fundamentalnych klas logiki biznesowej platformy enova365/Soneta Enterprise. Klasy te stanowią podstawę mapowania obiektowo-relacyjnego (ORM) i są niezbędne do tworzenia dodatków. Skill zawiera dokumentację fundamentalnych klas logiki biznesowej platformy enova365/Soneta Enterprise. Klasy te
stanowią podstawę mapowania obiektowo-relacyjnego (ORM) i są niezbędne do tworzenia kodu i dodatków.
## Architektura warstw ## Architektura warstw
``` ```
┌─────────────────────────────────────────────────────┐ BusApplication.Instance (singleton) - multihreaded
│ Interfejs graficzny (UI) │ └── Database
├─────────────────────────────────────────────────────┤ └── Login
│ Context │ ← Komunikacja UI ↔ logika └── Session - singlethreaded
├─────────────────────────────────────────────────────┤ └── Module
│ Logika biznesowa │ └── Table
│ ┌─────────────────────────────────────────────┐ │ └── Row
│ │ BusApplication (Singleton) │ │
│ │ └── Database (konfiguracja bazy) │ │
│ │ └── Login (uwierzytelnienie) │ │
│ │ └── Session (zarządzanie danymi) │ │
│ │ └── Module → Table → Row │ │
│ └─────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────┤
│ Baza danych SQL │
└─────────────────────────────────────────────────────┘
``` ```
## 3 poziomy logiki biznesowej ## 3 poziomy logiki biznesowej
@@ -69,8 +61,6 @@ Module (abstrakcyjna)
- `Context` - `Context`
- oraz wszystkie klasy pochodne - oraz wszystkie klasy pochodne
Każdy wątek powinien tworzyć własną sesję.
### Obiekty multi-threaded (można współdzielić) ### Obiekty multi-threaded (można współdzielić)
- `BusApplication` - `BusApplication`
- `Database` - `Database`
@@ -85,10 +75,6 @@ Session to kluczowa klasa do zarządzania danymi. **Każda operacja na danych wy
```csharp ```csharp
// Przez Login // Przez Login
Session session = login.CreateSession(readOnly: false, config: false, name: "MojaSesja"); Session session = login.CreateSession(readOnly: false, config: false, name: "MojaSesja");
// Parametry konstruktora:
// readOnly: true = tylko odczyt, false = edycja
// config: true = dane konfiguracyjne (cache), false = dane operacyjne (aktualne)
``` ```
### Typy sesji ### Typy sesji
@@ -151,6 +137,7 @@ Zmiany wykonywane są w trybie **optimistic-lock**:
- Sesja konfiguracyjna używa cache'a (optymalizacja odczytów) - Sesja konfiguracyjna używa cache'a (optymalizacja odczytów)
- Sesja operacyjna zawsze czyta z bazy (aktualność danych) - Sesja operacyjna zawsze czyta z bazy (aktualność danych)
- **Nie mieszaj obiektów z różnych sesji** - użyj `session.Get(obiekt)` aby doczytać obiekt w bieżącej sesji - **Nie mieszaj obiektów z różnych sesji** - użyj `session.Get(obiekt)` aby doczytać obiekt w bieżącej sesji
- ID identyfikuje obiekt w tabeli, może powtarzać się w różnych tabelach
## Klasa Module ## Klasa Module
@@ -258,10 +245,163 @@ public static TowaryModule GetTowary(this Session session)
```csharp ```csharp
// Extension method (prostsze) // Extension method (prostsze)
var tm = session.GetTowary(); var tm = session.GetTowary();
var tm = context.Session.GetTowary();
var tm = towar.Session.GetTowary();
// GetInstance (gdy mamy ISessionable, np. Row lub Context) // GetInstance (gdy mamy ISessionable, ale property Session jest niedostępne)
var tm = TowaryModule.GetInstance(context); var tm = TowaryModule.GetInstance(sessionable);
var tm = TowaryModule.GetInstance(towar); // towar implementuje ISessionable ```
## Kod biznesowy vs UI
Kod biznesowy realizuje operacje logiki biznesowej (jak backend).
Kod UI (fronend) 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) {...}`)
## Serwisy
Pozwalają tworzyć obiekty (komponenty), których czas życia będzie zależał od scope:
- App (BusApplication.Instance)
- Database
- Login
- Session (default - nie trzeba określać w deklaracji)
Umieszczając deklarację interface serwisu w assembly wspólnym, pozwalają na udostępnianie serwisów między modułami,
nawet gdy nie ma odpowiedniej referencji.
* **Tylko serwisy scope Session są single-threded, pozostałe są multi-threaded.**
* Serwis może być `IDisposable`.
* Dla serwisów App, Database, Login nie przechowuj obiektów sesyjnych.
* `[RequireOwnService]` tylko dla serwisów, które nie mogą być nadpisywane.
### Przykład deklaracji
```csharp
[assembly: Service<MyNamespace.IRegistry, MyNamespace.Registry>(ServiceScope.Login)]
namespace MyNamespace;
[RequireServiceScope(ServiceScope.Login)]
public interface IRegistry {
void Method();
}
internal sealed class Registry : IRegistry {
public void Method() {}
}
```
### Odczytanie serwisu w kodzie
```csharp
IRegistry registerRequired = login.GetRequiredService<IRegistry>();
IRegistry? registerOptional = login.GetService<IRegistry>();
foreach (IRegistry registers in login.GetServices<IRegistry>())
{
}
```
### Użycie serwisu w worker lub extender - obiekt tworzony przez Context.CreareObject()
```csharp
// Rozwiązanie lepsze
class MyWorker1(IRegistry registry) {
}
class MyWorker2 {
[Context]
private IRegistry Registry { get; set; }
}
```
## Metadane modułów, tabel, kluczy, pól, itp
Dostęp do metadanych obiektów biznesowych dostępny przez metody `static` klasy `ApplicationInfo`.
Odczytanie informacji o tabli `TableInfo info = ApplicationInfo.GetTableInfo(nazwaTabeli)`. Istnieje tylko jedna
referencja obiektu TableInfo dla tabeli. Można używać `ReferenceEquals`, `Dictionary`, itp.
Odczytanie wszystkich tabel `ApplicationInfo.GetTablesInfo()`, a np tabel dla modułu `ApplicationInfo.GetModuleInfo
(moduleName).TableInfos`.
### Wykorzystuj `TableInfo` do weryfikacji tabeli
```csharp
Row row1 = ...;
Row row2 = ...;
if (row1.Table.TableInfo==row2.Table.TableInfo) {
// Ta sama tabela, nawet gdy różne sesje
}
```
## Tłumaczenie i formatowanie napisów, tekstów i string
Biblioteka obsługuje słowniki tłumaczące napisy w aplikacji Soneta.
* Tłumaczone napisy muszą używać metody typu string-extender `"napis dotłumaczenia".Translate()`.
* Tłumaczenie tekstów formatowanych przez `"napis {0} z wartością {1}".TranslateFormat(arg0, arg1)`.
* Gdy string ma być zignorowany przez tłumacza, MUSISZ zaznaczyć do metodą `"nie tłumaczymy".TranslateIgnore()`,inaczej błąd kompilacji.
* Jeżeli w metodzie lub klasie jest więcej napisów do zignorowania użyj atrybutu `[TranslateIgnore]`.
* Parametr metody jest ignorowany przez tłumacza, użyj atrybutu `[TranslateIgnore]` na parametrze.
## Log zmian i obserwowalność
Używaj standardowy narzędzi do logowania `ILogger<T>`. Użyj `[TranslateIgnore]` w metodzie wywołującej log.
Używaj `logger.IsEnable(LogLevel)` kiedy parametry wymagają dodatkowych operacji.
### Użycie `ILogger` oraz exception log
```csharp
class Test
{
private readonly logger = BusApplication.Instance.GetRequiredService<ILogger<Test>>();
public decimal Kwota;
[TranslateIgnore]
public void Metoda()
{
try {
logger.LogInformation("Wywołanie metody {nazwa}", nameof(Validate));
if (kwota<0)
logger.LogWarning("Kwota {kwota} nie może być ujemna w metodzie '{metoda}'", Kwota, nameof(Validate));
}
catch (Exception ex) {
// Sposób na wrzucenie exception do trace
ex.Log<Test>();
throw;
}
}
}
```
### Śledzenie czasu wykonania z obsługą exception
```csharp
class Test {
private static readonly ActSource actSource = new(nameof(Test), ActSource.TraceLevel.Default);
public void Action() {
using var activity = actSource.Start();
try {
// Algorytm do śledzenia
}
catch (Exception ex) {
activity.AddExceptionWithError(ex);
throw;
}
}
}
``` ```
## Szczegółowa dokumentacja ## Szczegółowa dokumentacja
@@ -276,7 +416,7 @@ var tm = TowaryModule.GetInstance(towar); // towar implementuje ISessionable
### Odczyt danych ### Odczyt danych
```csharp ```csharp
using (var session = login.CreateSession(true, false, "Odczyt")) using (var session = login.CreateSession(readOnly: true, config: false, name: "Odczyt"))
{ {
var tm = session.GetTowary(); // Extension method var tm = session.GetTowary(); // Extension method
foreach (Towar t in tm.Towary.WgKodu) foreach (Towar t in tm.Towary.WgKodu)
@@ -289,7 +429,7 @@ using (var session = login.CreateSession(true, false, "Odczyt"))
### Tworzenie nowego obiektu ### Tworzenie nowego obiektu
```csharp ```csharp
using (var session = login.CreateSession(false, false, "Dodawanie")) using (var session = login.CreateSession(readOnly: false, config: false, name: "Dodawanie"))
{ {
var tm = session.GetTowary(); var tm = session.GetTowary();
@@ -309,7 +449,7 @@ using (var session = login.CreateSession(false, false, "Dodawanie"))
### Modyfikacja istniejącego obiektu ### Modyfikacja istniejącego obiektu
```csharp ```csharp
using (var session = login.CreateSession(false, false, "Edycja")) using (var session = login.CreateSession(readOnly: false, config: false, name: "Edycja"))
{ {
var tm = session.GetTowary(); var tm = session.GetTowary();
var towar = tm.Towary.WgKodu["STARY001"]; var towar = tm.Towary.WgKodu["STARY001"];
@@ -22,9 +22,7 @@ Przykładowa zawartość przy otwartej liście kontrahentów:
| `INavigatorContext` | Kontekst grida (zaznaczenia, focus) | | `INavigatorContext` | Kontekst grida (zaznaczenia, focus) |
| `View` | Źródło danych grida | | `View` | Źródło danych grida |
| `Params` | Klasa parametrów filtrów | | `Params` | Klasa parametrów filtrów |
| `LicencjaProgramu` | Informacje o licencji |
| `Login` | Zalogowany użytkownik | | `Login` | Zalogowany użytkownik |
| `MsSqlDatabase` | Baza danych |
## Odczyt z kontekstu ## Odczyt z kontekstu
@@ -41,7 +39,7 @@ public void Action(Context context)
} }
``` ```
### Przez indeksator (rzuca wyjątek gdy brak) ### Przez indeksator
```csharp ```csharp
public void Action(Context cx) public void Action(Context cx)
@@ -76,7 +74,7 @@ public void Action(Context cx)
```csharp ```csharp
public void Action(Context cx) public void Action(Context cx)
{ {
// Przez indeksator // Przez indeksator z określeniem typu
Kontrahent knt = ...; Kontrahent knt = ...;
cx[typeof(Kontrahent)] = knt; cx[typeof(Kontrahent)] = knt;
@@ -5,14 +5,14 @@ Dokumentacja klas zarządzających połączeniem z bazą danych i sesjami w plat
## Hierarchia obiektów ## Hierarchia obiektów
``` ```
BusApplication (Singleton) BusApplication.Instance (Singleton)
└── Database[] (kolekcja baz danych) └── Database[] (kolekcja baz danych)
└── Login (uwierzytelniony użytkownik) └── Login (uwierzytelniony użytkownik)
└── Session[] (sesje robocze) └── Session[] (sesje robocze)
└── Module → Table → Row └── Module → Table → Row
``` ```
## BusApplication ## Klasa BusApplication
Singleton reprezentujący instancję aplikacji ERP. Tworzony podczas inicjalizacji systemu. Singleton reprezentujący instancję aplikacji ERP. Tworzony podczas inicjalizacji systemu.
@@ -40,7 +40,7 @@ foreach (Database db in BusApplication.Instance)
| `Is365` | `bool` | `true` = wersja HTML, `false` = wersja okienkowa | | `Is365` | `bool` | `true` = wersja HTML, `false` = wersja okienkowa |
| `this[string]` | `Database` | Indeksator - baza po nazwie | | `this[string]` | `Database` | Indeksator - baza po nazwie |
## Database ## Klasa Database
Abstrakcyjna klasa reprezentująca bazę danych. Konkretne implementacje dla wspieranych silników: Abstrakcyjna klasa reprezentująca bazę danych. Konkretne implementacje dla wspieranych silników:
@@ -84,9 +84,9 @@ Login login = db.Login(new LoginParameters
}); });
``` ```
## Login ## Klasa Login
Obiekt reprezentujący zalogowanego użytkownika. Zarządza sesjami. Obiekt reprezentujący zalogowanego użytkownika. Zarządza sesjami. IDisposable.
### Tworzenie ### Tworzenie
@@ -109,7 +109,7 @@ Login login = db.Login(new LoginParameters
// var ent = login.Entitle; // unikać! // var ent = login.Entitle; // unikać!
// ZALECANE: Użyj w konkretnej sesji // ZALECANE: Użyj w konkretnej sesji
using (var session = login.CreateSession(true, false, "Odczyt")) using (var session = login.CreateSession(readOnly: true, config: false, name: "Odczyt"))
{ {
var op = session.AuthorizationInfo.Operator; var op = session.AuthorizationInfo.Operator;
Console.WriteLine($"Zalogowany: {op.Name} - {op.FullName}"); Console.WriteLine($"Zalogowany: {op.Name} - {op.FullName}");
@@ -153,20 +153,20 @@ var opisDlaSzt = login.ExecuteConfig(configSession =>
### Sygnatury CreateSession ### Sygnatury CreateSession
```csharp ```csharp
public Session CreateSession(bool readOnly, bool config, string name) public Session CreateSession(bool readOnly, bool config, string name) // rekomendowana
public Session CreateSession(bool readOnly, bool config) public Session CreateSession(bool readOnly, bool config)
public Session CreateSession() // readOnly=false, config=false public Session CreateSession() // readOnly=false, config=false
``` ```
## Session ## Klasa Session
Fundamentalna klasa do zarządzania danymi. **Każda operacja na danych wymaga sesji.** Fundamentalna klasa do zarządzania danymi. **Każda operacja na danych wymaga sesji.** IDisposable.
### Tworzenie ### Tworzenie
```csharp ```csharp
// ZAWSZE używaj using lub wywołuj Dispose() // ZAWSZE używaj using lub wywołuj Dispose()
using (var session = login.CreateSession(false, false, "MojaSesja")) using (var session = login.CreateSession(readOnly: false, config: false, name: "MojaSesja"))
{ {
// operacje na danych // operacje na danych
session.Save(); session.Save();
@@ -218,8 +218,8 @@ session.Save();
```csharp ```csharp
// Normalne zjawisko - wiele sesji może współistnieć // Normalne zjawisko - wiele sesji może współistnieć
using (var session1 = login.CreateSession(true, false, "Lista1")) using (var session1 = login.CreateSession(readOnly: true, config: false, name: "Lista1"))
using (var session2 = login.CreateSession(true, false, "Lista2")) using (var session2 = login.CreateSession(readOnly: true, config: false, name: "Lista2"))
{ {
// Obie sesje mogą odczytywać te same dane // Obie sesje mogą odczytywać te same dane
var tm1 = session1.GetTowary(); var tm1 = session1.GetTowary();
@@ -232,7 +232,7 @@ using (var session2 = login.CreateSession(true, false, "Lista2"))
**WAŻNE:** Każda zmiana obiektu biznesowego MUSI być w transakcji! **WAŻNE:** Każda zmiana obiektu biznesowego MUSI być w transakcji!
```csharp ```csharp
using (var session = login.CreateSession(false, false, "Edycja")) using (var session = login.CreateSession(readOnly: false, config: false, name: "Edycja"))
{ {
var tm = session.GetTowary(); var tm = session.GetTowary();
var towar = tm.Towary.WgKodu["KOD001"]; var towar = tm.Towary.WgKodu["KOD001"];
@@ -254,7 +254,7 @@ using (var session = login.CreateSession(false, false, "Edycja"))
### Logout(editMode: false) - transakcja tylko do odczytu ### Logout(editMode: false) - transakcja tylko do odczytu
```csharp ```csharp
using (var session = login.CreateSession(false, false, "Przeglad")) using (var session = login.CreateSession(readOnly: false, config: false, name: "Przeglad"))
{ {
var tm = session.GetTowary(); var tm = session.GetTowary();
var towar = tm.Towary.WgKodu["KOD001"]; var towar = tm.Towary.WgKodu["KOD001"];
@@ -305,7 +305,7 @@ Login login = db.Login(new LoginParameters
}); });
// 4. Utworzenie sesji (zawsze z nazwą!) // 4. Utworzenie sesji (zawsze z nazwą!)
using (var session = login.CreateSession(false, false, "Import")) using (var session = login.CreateSession(readOnly: false, config: false, name: "Import"))
{ {
// 5. Pobranie modułu (extension method) // 5. Pobranie modułu (extension method)
var tm = session.GetTowary(); var tm = session.GetTowary();
@@ -342,7 +342,7 @@ using (var session = login.CreateSession(false, false, "Import"))
Login sharedLogin = db.Login(new LoginParameters { UserName = "admin", UserPassword = "haslo" }); Login sharedLogin = db.Login(new LoginParameters { UserName = "admin", UserPassword = "haslo" });
Parallel.ForEach(items, item => { Parallel.ForEach(items, item => {
using (var session = sharedLogin.CreateSession(false, false, "Watek")) using (var session = sharedLogin.CreateSession(readOnly: false, config: false, name: "Watek"))
{ {
var tm = session.GetTowary(); var tm = session.GetTowary();
using (var transaction = session.Logout(editMode: true)) using (var transaction = session.Logout(editMode: true))
@@ -354,13 +354,3 @@ Parallel.ForEach(items, item => {
} }
}); });
``` ```
## Sesja bez interfejsu graficznego
Sesja działa w warstwie logiki biznesowej - **nie wymaga UI**.
Przykłady użycia bez interfejsu:
- **Harmonogram Zadań** - usługa Windows wykonująca operacje bazodanowe
- **Importy danych** - procesy wsadowe
- **Testy jednostkowe** - automatyczne testy logiki
- **API REST** - obsługa żądań HTTP