Porządki i usprawnienia
This commit is contained in:
+22
-22
@@ -1,12 +1,25 @@
|
|||||||
---
|
---
|
||||||
name: soneta-programming
|
name: soneta-programming
|
||||||
description: >
|
description: >
|
||||||
Fundamentalne klasy ORM platformy enova365/Soneta Enterprise. Obejmuje mapowanie
|
Fundamentalne klasy ORM oraz wzorce kodu biznesowego platformy enova365 / Soneta
|
||||||
obiektowo-relacyjne (Row, Table, Module), zarządzanie sesją (Session), logowanie
|
Enterprise / Triva. Obejmuje mapowanie obiektowo-relacyjne (Row, Table, Module,
|
||||||
(Login, Database, BusApplication), paczki danych (Datapack, GuidedRow) oraz
|
GuidedRow, ExportedRow), zarządzanie sesją i transakcjami (Session, Logout,
|
||||||
kontekst (Context). Używaj gdy użytkownik pyta o podstawowe klasy logiki biznesowej,
|
Commit / CommitUI, Save, optimistic locking), logowanie (Login, Database,
|
||||||
strukturę obiektów ORM, sesje i transakcje, hierarchię klas Row/Table/Module,
|
BusApplication), paczki danych (Datapack, GuidedRow, ChangeInfos), serwerowe
|
||||||
mechanizm Datapack i synchronizację danych, lub kontekst aplikacji enova365.
|
filtrowanie LINQ (RowCondition, SubTable[condition]), kontekst i parametry
|
||||||
|
(Context, ContextBase), rozszerzenia modelu (Worker, Extender, [Action]),
|
||||||
|
widoki list (ViewInfo, FolderView, CreateView), cechy (Features), tłumaczenia
|
||||||
|
(Translate, ILogger), action result oraz zasady bezpiecznego kodu biznesowego
|
||||||
|
(safe-code, checklist do code review). Używaj **zawsze** gdy użytkownik:
|
||||||
|
(1) pisze, modyfikuje lub refaktoruje kod biznesowy enova365 / Soneta /
|
||||||
|
Triva — nawet jeśli nie wymienia nazw klas wprost; (2) pyta o Session,
|
||||||
|
Row, Table, Module, Login, Database, BusApplication, Context, Datapack,
|
||||||
|
GuidedRow, Worker, Extender, ViewInfo, RowCondition; (3) wspomina sesje,
|
||||||
|
transakcje biznesowe, Logout, Commit, Save, optimistic lock, blokady wierszy;
|
||||||
|
(4) prosi o code review lub bezpieczeństwo kodu biznesowego Soneta; (5)
|
||||||
|
wspomina pisanie dodatku, modułu, importu, workera, ekstendera, akcji w menu
|
||||||
|
Czynności, folderu/listy; (6) pyta o thread-safety, sesje konfiguracyjne
|
||||||
|
(ExecuteConfig), różnice dane konfiguracyjne vs operacyjne.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Soneta Programming Basics - Podstawowe klasy ORM
|
# Soneta Programming Basics - Podstawowe klasy ORM
|
||||||
@@ -320,23 +333,10 @@ Więcej wzorców (kasowanie, obsługa błędów, pełny import end-to-end) - pat
|
|||||||
|
|
||||||
## Narzędzia pomocnicze
|
## Narzędzia pomocnicze
|
||||||
|
|
||||||
Skill udostępnia skrypt `scripts/scan-props.csx` (uruchamiany przez `dotnet script`) do odczytu publicznych pól klasy zagnieżdżonej `XxxModule+XxxRecord` ze skompilowanych DLL dodatku — bez ładowania IL do CLR (Roslyn `MetadataReference.CreateFromFile`). Wypisuje również właściwości klasy biznesowej (kalkulowane), Caption/Description oraz rekurencyjnie rozwija pola typu subrow z notacją kropkową.
|
Skill udostępnia dwa skrypty `dotnet script` (`scripts/`) do statycznej inwentaryzacji bibliotek Soneta — bez ładowania IL do CLR (Roslyn `MetadataReference.CreateFromFile`):
|
||||||
|
|
||||||
```bash
|
- `scan-modules.csx` — listuje moduły (`*Module`) i ich tabele (`*Row`/`*Table`) z Caption/Description. Dobre na start. Szczegóły, parametry i przykłady uruchomienia: [references/scan-modules.md](references/scan-modules.md).
|
||||||
dotnet script ~/.claude/skills/soneta-programming/scripts/scan-props.csx \
|
- `scan-props.csx` — wypisuje pola i właściwości kalkulowane konkretnej klasy biznesowej, rekurencyjnie po polach typu subrow. Sięgnij po niego, gdy znasz już tabelę i potrzebujesz jej kontraktu. Szczegóły: [references/scan-props.md](references/scan-props.md).
|
||||||
-- DokumentHandlowy ./bin/Debug/net8.0
|
|
||||||
```
|
|
||||||
|
|
||||||
Szczegóły, kody wyjścia i ograniczenia: [references/scan-props.md](references/scan-props.md).
|
|
||||||
|
|
||||||
Drugi skrypt — `scripts/scan-modules.csx` — listuje wszystkie moduły (`*Module` dziedziczące z `Soneta.Business.Module`) oraz znajdujące się w nich tabele (`RowType`/`TableType` z Caption/Description). Pomocne przy wstępnej inwentaryzacji bibliotek, zanim sięgniesz po `scan-props.csx` dla konkretnej tabeli.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dotnet script ~/.claude/skills/soneta-programming/scripts/scan-modules.csx \
|
|
||||||
-- ./bin/Debug/net8.0
|
|
||||||
```
|
|
||||||
|
|
||||||
Szczegóły: [references/scan-modules.md](references/scan-modules.md).
|
|
||||||
|
|
||||||
## Konwencje nazewnicze
|
## Konwencje nazewnicze
|
||||||
|
|
||||||
|
|||||||
@@ -17,15 +17,11 @@ Praktyczne przykłady użycia podstawowych klas logiki biznesowej enova365/Sonet
|
|||||||
|
|
||||||
## Ważne zasady
|
## Ważne zasady
|
||||||
|
|
||||||
### Thread-safety
|
Ten plik zakłada znajomość fundamentów — przed sięgnięciem po przykłady upewnij się, że są jasne:
|
||||||
|
|
||||||
**Obiekty single-threaded** - nie współdziel między wątkami:
|
- **Thread-safety** (`Session`/`Row`/`Table`/`Module`/`Context` są single-threaded; `BusApplication`/`Database`/`Login` można współdzielić) — patrz [session-login.md](session-login.md).
|
||||||
- `Session`, `Module`, `Table`, `Row`, `Context`
|
- **Transakcje biznesowe** — każda zmiana obiektu (dodanie, modyfikacja property, kasowanie) wymaga otwartej transakcji `session.Logout(editMode: true)` zakończonej `Commit()`; zapis do bazy przez `session.Save()` poza transakcją. Szczegóły, w tym typy sesji, `CommitUI()` i optimistic locking — patrz [session-login.md](session-login.md).
|
||||||
|
- **Bezpieczny kod** — przy nowym kodzie i review weryfikuj reguły z [safe-code.md](safe-code.md).
|
||||||
**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
|
### Extension methods dla modułów
|
||||||
|
|
||||||
@@ -39,22 +35,6 @@ var kadry = session.GetKadry();
|
|||||||
var bm = session.GetBusiness();
|
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
|
## Dostęp do danych
|
||||||
|
|
||||||
### Odczyt listy towarów
|
### Odczyt listy towarów
|
||||||
|
|||||||
@@ -4,6 +4,29 @@
|
|||||||
|
|
||||||
Najwygodniejsze API to budowa warunku z wyrażeń LINQ (`Expression<Predicate<TRow>>`) przez `RowCondition.FromExpression(...)` oraz aplikowanie wyrażeń bezpośrednio do `SubTable` i `View` przez indeksator i `AddExpression(...)`.
|
Najwygodniejsze API to budowa warunku z wyrażeń LINQ (`Expression<Predicate<TRow>>`) przez `RowCondition.FromExpression(...)` oraz aplikowanie wyrażeń bezpośrednio do `SubTable` i `View` przez indeksator i `AddExpression(...)`.
|
||||||
|
|
||||||
|
## Spis treści
|
||||||
|
|
||||||
|
- [Najważniejsze zasady](#najważniejsze-zasady)
|
||||||
|
- [Wzorce użycia w kodzie](#wzorce-użycia-w-kodzie)
|
||||||
|
- [1. Indeksator `SubTable[expression]` - logika biznesowa](#1-indeksator-subtableexpression---logika-biznesowa)
|
||||||
|
- [2. `View.AddExpression(...)` - listy w UI](#2-viewaddexpression---listy-w-ui)
|
||||||
|
- [3. `Query.Table.AddExpression(...)` - zapytania niskopoziomowe](#3-querytableaddexpression---zapytania-niskopoziomowe)
|
||||||
|
- [4. `RowCondition.FromExpression(...)` - jawne budowanie warunku](#4-rowconditionfromexpression---jawne-budowanie-warunku)
|
||||||
|
- [Zakres możliwych wyrażeń](#zakres-możliwych-wyrażeń)
|
||||||
|
- [Odwołania do pól](#odwołania-do-pól)
|
||||||
|
- [Wartości po stronie klienta](#wartości-po-stronie-klienta)
|
||||||
|
- [Typy proste, enum, int](#typy-proste-enum-int)
|
||||||
|
- [Bool](#bool)
|
||||||
|
- [String](#string)
|
||||||
|
- [Null / not null](#null--not-null)
|
||||||
|
- [Referencje](#referencje)
|
||||||
|
- [Operator IN - przynależność do zbioru](#operator-in---przynależność-do-zbioru)
|
||||||
|
- [Operatory logiczne i wyrażenia złożone](#operatory-logiczne-i-wyrażenia-złożone)
|
||||||
|
- [Pola złożone (Quantity, Currency, FromTo)](#pola-złożone-quantity-currency-fromto)
|
||||||
|
- [Kolekcje powiązane (podlisty)](#kolekcje-powiązane-podlisty)
|
||||||
|
- [Ograniczenia - co się nie skompiluje do SQL](#ograniczenia---co-się-nie-skompiluje-do-sql)
|
||||||
|
- [Kiedy używać czego](#kiedy-używać-czego)
|
||||||
|
|
||||||
## Najważniejsze zasady
|
## Najważniejsze zasady
|
||||||
|
|
||||||
* W wyrażeniu LINQ można odwoływać się **wyłącznie do pól bazodanowych** (kolumn tabeli, pól złożonych, kolekcji powiązanych, cech). Próba użycia pola niebazodanowego rzuca `LinqConditionException`.
|
* W wyrażeniu LINQ można odwoływać się **wyłącznie do pól bazodanowych** (kolumn tabeli, pól złożonych, kolekcji powiązanych, cech). Próba użycia pola niebazodanowego rzuca `LinqConditionException`.
|
||||||
|
|||||||
@@ -114,7 +114,8 @@ Wyjątek po `Commit()` ale przed `Save()` nie wycofuje zmian z bieżącej sesji
|
|||||||
### 5.2 Komunikaty walidacyjne przez `Translate`
|
### 5.2 Komunikaty walidacyjne przez `Translate`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
throw new RowException(this, "Pole {0} jest wymagane".Translate(), nameof(Nazwa));
|
throw new RowException(this, "Pole jest wymagane".Translate());
|
||||||
|
throw new RowException(this, "Pole {0} jest wymagane".TranslateFormat(nameof(Nazwa)));
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
Dokumentacja klas zarządzających połączeniem z bazą danych i sesjami w platformie enova365/Soneta.
|
Dokumentacja klas zarządzających połączeniem z bazą danych i sesjami w platformie enova365/Soneta.
|
||||||
|
|
||||||
|
## Spis treści
|
||||||
|
|
||||||
|
- [Hierarchia obiektów](#hierarchia-obiektów)
|
||||||
|
- [Klasa BusApplication](#klasa-busapplication) — singleton, dostęp do instancji, właściwości
|
||||||
|
- [Klasa Database](#klasa-database) — konfiguracja, logowanie do bazy
|
||||||
|
- [Klasa Login](#klasa-login) — tworzenie, dostęp do operatora, tworzenie sesji, dane konfiguracyjne, sygnatury `CreateSession`
|
||||||
|
- [Klasa Session](#klasa-session) — tworzenie, typy sesji, właściwości, zarządzanie danymi, sesje konfiguracyjne, wiele sesji, transakcje (`Logout`, `Commit`/`CommitUI`)
|
||||||
|
- [Kompletny przykład](#kompletny-przykład)
|
||||||
|
- [Thread-safety](#thread-safety) — które obiekty można współdzielić, które nie
|
||||||
|
|
||||||
## Hierarchia obiektów
|
## Hierarchia obiektów
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -229,7 +239,7 @@ using (var session2 = login.CreateSession(readOnly: true, config: false, name: "
|
|||||||
|
|
||||||
### Transakcje biznesowe - Session.Logout()
|
### Transakcje biznesowe - Session.Logout()
|
||||||
|
|
||||||
**WAŻNE:** Każda zmiana obiektu biznesowego MUSI być w transakcji!
|
**Każda modyfikacja obiektu MUSI być w transakcji biznesowej** otwieranej przez `Session.Logout(editMode: true)` — dotyczy dodawania, modyfikacji właściwości oraz kasowania.
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using (var session = login.CreateSession(readOnly: false, config: false, name: "Edycja"))
|
using (var session = login.CreateSession(readOnly: false, config: false, name: "Edycja"))
|
||||||
|
|||||||
@@ -7,6 +7,19 @@ ViewInfo to **kod UI** (patrz "Kod biznesowy vs UI" w głównym `SKILL.md`).
|
|||||||
Skill `soneta-form-xml` opisuje pełną składnię plików `viewform.xml`/`pageform.xml` (elementy `DataForm`, `Flow`,
|
Skill `soneta-form-xml` opisuje pełną składnię plików `viewform.xml`/`pageform.xml` (elementy `DataForm`, `Flow`,
|
||||||
`Grid`, `Field`, `Appearance`, `GroupBy`, atrybuty `EditValue`, `Visibility`, `IsReadOnly`, `Condition` itd.). Sięgaj do niego za każdym razem, gdy edytujesz lub generujesz XML formularza — bez tej wiedzy łatwo wygenerować nieprawidłowe znaczniki.
|
`Grid`, `Field`, `Appearance`, `GroupBy`, atrybuty `EditValue`, `Visibility`, `IsReadOnly`, `Condition` itd.). Sięgaj do niego za każdym razem, gdy edytujesz lub generujesz XML formularza — bez tej wiedzy łatwo wygenerować nieprawidłowe znaczniki.
|
||||||
|
|
||||||
|
## Spis treści
|
||||||
|
|
||||||
|
- [Rejestracja folderu — atrybut `FolderView`](#rejestracja-folderu--atrybut-folderview)
|
||||||
|
- [Anatomia klasy ViewInfo](#anatomia-klasy-viewinfo)
|
||||||
|
- [Eventy — gdzie zaszywać logikę](#eventy--gdzie-zaszywać-logikę)
|
||||||
|
- [Kanoniczna para `InitContext` + `CreateView`](#kanoniczna-para-initcontext--createview)
|
||||||
|
- [Klasa parametrów (`Params` / `WParams` / lub inna nazwa)](#klasa-parametrów-params--wparams--lub-inna-nazwa)
|
||||||
|
- [Filtrowanie View — paleta narzędzi](#filtrowanie-view--paleta-narzędzi)
|
||||||
|
- [Filtrowanie po cechach (Features)](#filtrowanie-po-cechach-features)
|
||||||
|
- [Powiązanie z `viewform.xml`](#powiązanie-z-viewformxml)
|
||||||
|
- [Pełny przykład minimalny](#pełny-przykład-minimalny)
|
||||||
|
- [Pułapki i dobre praktyki](#pułapki-i-dobre-praktyki)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Rejestracja folderu — atrybut `FolderView`
|
## Rejestracja folderu — atrybut `FolderView`
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ Oba korzystają z [Context](context.md) do pobierania parametrów.
|
|||||||
|
|
||||||
## Obiekty Worker
|
## Obiekty Worker
|
||||||
|
|
||||||
Dodają dodatkowe properties wyliczane do obiektów, które mogą być stosowane w bindowaniu lub pozwalają na
|
Worker dorzuca do obiektu danych dodatkowe properties wyliczane (do użycia w bindowaniu) oraz pozycje w menu Czynności.
|
||||||
definiowanie pozycji w menu Czynności.
|
|
||||||
|
|
||||||
* Worker jest zawsze przypisany do obiektu danych.
|
* Przypisuj worker do konkretnego obiektu danych — worker zawsze działa w kontekście jednego typu.
|
||||||
* W nazwie klasy powinno się stosować sufiks `Worker`
|
* Dodawaj do nazwy klasy sufiks `Worker` (np. `WyliczenieStanMagazynuWorker`).
|
||||||
* Nazwa klasy worker powinna określać jego działanie.
|
* Wybieraj nazwę klasy opisującą działanie, nie technikę.
|
||||||
* Może być inicjowany z context za pomocą `[Context]`
|
* Inicjuj parametry z kontekstu przez `[Context]`.
|
||||||
* Rejestracja za pomocą atrybutu assembly z dwoma parametrami `[Worker<WorkerType, DataType>]` - zalecana wersja generic
|
* Rejestruj przez generyczny atrybut `[assembly: Worker<WorkerType, DataType>]` — to wersja zalecana.
|
||||||
|
|
||||||
### Rejestracja worker
|
### Rejestracja worker
|
||||||
|
|
||||||
@@ -95,7 +94,7 @@ public class SendEmailsForKontrahentWorker
|
|||||||
int counter = 0;
|
int counter = 0;
|
||||||
foreach (var k in Kontrahenci)
|
foreach (var k in Kontrahenci)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(k.Email))
|
if (!k.Email.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
WyslijEmail(k.Email);
|
WyslijEmail(k.Email);
|
||||||
++counter;
|
++counter;
|
||||||
|
|||||||
Reference in New Issue
Block a user