262 lines
13 KiB
Markdown
262 lines
13 KiB
Markdown
# HANDEL07 — Cechy (Features)
|
|
|
|
> Wspólne fakty o typie, podstawowe typy i szablon wzorca: [../handel.md](../handel.md).
|
|
|
|
Cechy (Features) to dodatkowe, definiowalne informacje przypisane do `Row` — tu: do dokumentu
|
|
(`DokumentHandlowy`) i pozycji (`PozycjaDokHandlowego`). Definicje cech (`FeatureDefinition`) tworzy
|
|
się we wdrożeniu (bez konwersji bazy); cecha jest adresowana **po nazwie definicji**. Dostęp daje
|
|
property `Features` (`Soneta.Business.FeatureCollection`) oraz nietypowany indeksator `Row["Nazwa"]`.
|
|
Fundamenty cech opisuje `references/features.md` — tu pokazujemy ich użycie na dokumencie handlowym.
|
|
|
|
> Cechy są częścią publicznego kontraktu. **Samo przenoszenie cech** (z partii / z dokumentu
|
|
> nadrzędnego) jest sterowane **konfiguracją definicji dokumentu/relacji**, a nie wywoływane
|
|
> imperatywnie z dodatku — patrz HANDEL-W40.
|
|
|
|
---
|
|
|
|
### HANDEL-W40 — Przenoszenie cech z partii (dostawy) / towaru na pozycję dokumentu
|
|
|
|
**Cel:** sprawić, by przy rozchodzie magazynowym cechy zapisane na partii (dostawie) trafiły na
|
|
pozycję dokumentu rozchodowego, a przy przekształceniach w relacjach — by cechy dokumentu/pozycji
|
|
nadrzędnej zostały skopiowane na dokument podrzędny. To mechanizm **konfiguracyjny**: ustawiasz flagi
|
|
na `DefDokHandlowego` / definicji relacji, platforma kopiuje cechy automatycznie podczas operacji.
|
|
|
|
**Warianty:**
|
|
|
|
| Wariant | Gdzie ustawić | Pole / mechanizm |
|
|
|---|---|---|
|
|
| Partia (dostawa) → pozycja rozchodu | definicja dokumentu rozchodowego (WZ/RW/FV) | `DefDokHandlowego.KopiujCechyDostawy: bool` |
|
|
| Dokument nadrzędny → podrzędny (cechy nagłówka) | definicja relacji | `KopiujCechyDokumentu: bool` |
|
|
| Dokument nadrzędny → podrzędny (cechy pozycji) | definicja relacji | `KopiujCechyPozycji: bool` |
|
|
| Wybrane cechy + synchronizacja zwrotna | definicja relacji | konfiguracja „kopiuj cechy" z listą definicji + flagą synchronizacji |
|
|
| Ręczne dopisanie cechy na pozycji | kod dodatku | `poz["Nazwa"] = wartość` w transakcji (HANDEL-W41) |
|
|
|
|
**Pola i typy:**
|
|
- `DefDokHandlowego.KopiujCechyDostawy: bool` — „Kopiuj cechy z dostawy"; włącza przeniesienie cech
|
|
partii na pozycję dokumentu **rozchodowego** przy wskazaniu zasobu / księgowaniu rozchodu.
|
|
- Na definicji relacji: `KopiujCechyDokumentu: bool`, `KopiujCechyPozycji: bool` — wymuszają
|
|
kopiowanie cech (nagłówka / pozycji) z dokumentu nadrzędnego na podrzędny.
|
|
- `poz.Features` / `poz["Nazwa"]` — odczyt/zapis cechy pozycji (typ `FeatureCollection` / `object`).
|
|
- Warunkiem działania jest istnienie **tej samej definicji cechy** zarejestrowanej dla obu tabel
|
|
(`PozycjeDokHan`, ewentualnie partia/towar) — kopiowane są cechy o zgodnej nazwie.
|
|
|
|
**Snippet:**
|
|
|
|
```csharp
|
|
// Włączenie przenoszenia cech z dostawy na pozycję rozchodu — konfiguracja definicji WZ.
|
|
// (jednorazowo, na etapie wdrożenia; wykonywane w sesji KONFIGURACYJNEJ)
|
|
var handel = session.GetHandel();
|
|
var defWZ = handel.DefDokHandlowych.WgSymbolu["WZ"];
|
|
|
|
using (var t = session.Logout(editMode: true))
|
|
{
|
|
defWZ.KopiujCechyDostawy = true; // cechy partii trafią na pozycję dokumentu rozchodowego
|
|
t.Commit();
|
|
}
|
|
session.Save();
|
|
|
|
// Po włączeniu flagi: tworzysz przyjęcie z cechą partii, a przy rozchodzie (wskazanie zasobu)
|
|
// cecha jest kopiowana na pozycję automatycznie — nie kopiujesz jej w kodzie.
|
|
// Przyjęcie (PW/PZ) — cecha "NrSerii" zapisana na pozycji = cecha dostawy/partii:
|
|
using (var t = session.Logout(editMode: true))
|
|
{
|
|
var pw = new DokumentHandlowy();
|
|
session.AddRow(pw);
|
|
pw.Definicja = handel.DefDokHandlowych.WgSymbolu["PW"];
|
|
pw.Magazyn = session.GetMagazyny().Magazyny.WgSymbol["F"];
|
|
|
|
var poz = new PozycjaDokHandlowego(pw);
|
|
session.AddRow(poz);
|
|
poz.Towar = session.GetTowary().Towary.WgKodu["BIKINI"];
|
|
poz.Ilosc = new Quantity(10, poz.Ilosc.Symbol);
|
|
poz.Cena = new DoubleCy(5m, poz.Cena.Symbol);
|
|
poz["NrSerii"] = "S-2026-001"; // cecha partii (definicja "NrSerii" dla PozycjeDokHan)
|
|
|
|
pw.Stan = StanDokumentuHandlowego.Zatwierdzony;
|
|
t.Commit();
|
|
}
|
|
session.Save(); // dopiero teraz powstaje zasób/partia z cechą
|
|
|
|
// Rozchód WZ ze wskazaniem partii — cecha "NrSerii" pojawi się na pozycji WZ
|
|
// dzięki KopiujCechyDostawy = true (kopiowane przez platformę przy księgowaniu rozchodu).
|
|
```
|
|
|
|
**Pułapki:**
|
|
- Przeniesienie cech z dostawy to **konfiguracja**, nie API: bez `KopiujCechyDostawy = true` na
|
|
definicji dokumentu rozchodowego nic się nie skopiuje — nie próbuj „przepisywać" cech partii
|
|
imperatywnie z dodatku.
|
|
- Kopiowane są cechy o **tej samej nazwie definicji** zarejestrowane dla pozycji; definicja cechy
|
|
musi istnieć przed użyciem (inaczej `poz["Nazwa"] = …` rzuci wyjątek — patrz HANDEL-W41).
|
|
- Cecha partii „materializuje się" dopiero po `Session.Save()` dokumentu przychodowego (to wtedy
|
|
powstaje zasób/obrót). Wskazanie partii przy rozchodzie i kopiowanie cechy działa na **zapisanych**
|
|
zasobach (Demo blokuje stan ujemny — rozchód wymaga wcześniejszego zapisanego przyjęcia).
|
|
- Kopiowanie nadrzędny→podrzędny w relacjach (`KopiujCechyDokumentu`/`KopiujCechyPozycji`) ustawia
|
|
się na **definicji relacji**, nie na definicji dokumentu; faktyczne tworzenie podrzędnego rób przez
|
|
`IRelacjeService` (sekcja relacji), a cechy dojdą same.
|
|
- Konfigurację definicji rób w sesji **konfiguracyjnej** (`config: true`) — to dane konfiguracyjne,
|
|
nie operacyjne (`safe-code.md`).
|
|
|
|
---
|
|
|
|
### HANDEL-W41 — Odczyt i zapis cech dokumentu / pozycji (`Features`)
|
|
|
|
**Cel:** odczytać i ustawić wartości cech na dokumencie handlowym i jego pozycjach — zarówno
|
|
nietypowano (po nazwie definicji), jak i typowano (gettery `FeatureCollection`).
|
|
|
|
**Warianty:**
|
|
|
|
| Wariant | Dostęp | Zwraca / przyjmuje |
|
|
|---|---|---|
|
|
| Odczyt nietypowany | `dok["Nazwa"]`, `poz["Nazwa"]` | `object` (`null`, gdy brak wartości) |
|
|
| Odczyt typowany | `dok.Features.GetString/GetInt/GetDecimal/GetDate/GetBool/GetCurrency/GetDoubleCy/GetPercent/GetAmount(...)` | konkretny typ Soneta |
|
|
| Zapis (dowolny typ) | `dok["Nazwa"] = wartość` w transakcji | — |
|
|
| Sprawdzenie istnienia | `dok.Features.Exists("Nazwa")` | `bool` |
|
|
| Usunięcie wartości | `dok.Features.Remove("Nazwa")` w transakcji | — |
|
|
| Kopiowanie całego zestawu | `źródło.Features.CopyTo(cel.Features)` | — |
|
|
| Lista definicji | `dok.Features.Definitions` | `FeatureDefinitions` |
|
|
|
|
**Pola i typy:**
|
|
- `DokumentHandlowy.Features: Soneta.Business.FeatureCollection`,
|
|
`PozycjaDokHandlowego.Features: Soneta.Business.FeatureCollection`.
|
|
- Indeksator nietypowany: `object this[string name]` na `Row` (`dok["Nazwa"]`) — równoważny
|
|
`dok.Features["Nazwa"]`.
|
|
- Gettery typowane (wybór): `GetString`, `GetInt`, `GetBool`, `GetDecimal`, `GetDouble`, `GetDate`,
|
|
`GetTime`, `GetFromTo`, `GetFraction`, `GetPercent`, `GetCurrency`, `GetDoubleCy`,
|
|
`GetDictionaryItem`, `GetRow`, `GetHistory`, `GetArray`.
|
|
- Pomocnicze: `Exists(string)`, `Remove(string)`, `IsChanged`, `Definitions`.
|
|
|
|
**Snippet:**
|
|
|
|
```csharp
|
|
var handel = session.GetHandel();
|
|
var dok = handel.DokHandlowe.WgDaty[...]; // lub Get<DokumentHandlowy>(guid) w testach
|
|
|
|
// --- Odczyt nietypowany (object; null gdy brak wartości) ---
|
|
object centrum = dok["CentrumKosztow"];
|
|
if (centrum == null) { /* cecha bez wartości na tym dokumencie */ }
|
|
|
|
// --- Odczyt typowany przez Features ---
|
|
string opis = dok.Features.GetString("OpisDodatkowy");
|
|
Date dostawa = dok.Features.GetDate("DataDostawy");
|
|
bool pilne = dok.Features.GetBool("Pilne");
|
|
|
|
// pozycja:
|
|
PozycjaDokHandlowego poz = dok.Pozycje.Cast<PozycjaDokHandlowego>().First();
|
|
string nrSerii = poz.Features.GetString("NrSerii");
|
|
|
|
// --- Zapis cech: wymaga transakcji edycyjnej (jak każda modyfikacja Row) ---
|
|
using (var t = session.Logout(editMode: true))
|
|
{
|
|
dok["OpisDodatkowy"] = "Pilna realizacja"; // String
|
|
dok["Pilne"] = true; // Bool
|
|
dok["DataDostawy"] = Date.Today.AddDays(3); // Date
|
|
poz["NrSerii"] = "S-2026-001"; // String na pozycji
|
|
t.Commit(); // CommitUI() w workerze/extenderze
|
|
}
|
|
session.Save();
|
|
|
|
// Istnienie / usunięcie wartości:
|
|
bool ma = dok.Features.Exists("OpisDodatkowy");
|
|
using (var t = session.Logout(editMode: true))
|
|
{
|
|
dok.Features.Remove("OpisDodatkowy");
|
|
t.Commit();
|
|
}
|
|
session.Save();
|
|
```
|
|
|
|
**Pułapki:**
|
|
- Cecha musi mieć **wcześniej utworzoną definicję** (`FeatureDefinition`) zarejestrowaną dla
|
|
właściwej tabeli (`DokHandlowe` dla dokumentu, `PozycjeDokHan` dla pozycji). Odwołanie do
|
|
niezdefiniowanej cechy rzuca wyjątek — to nie to samo co pole natywne.
|
|
- Każdy **zapis** cechy to modyfikacja `Row` → musi być w transakcji (`session.Logout(true)` +
|
|
`Commit`/`CommitUI`), potem `Save`. Odczyt transakcji nie wymaga.
|
|
- Indeksator nietypowany zwraca `object`; dla wartości pieniężnych/ilościowych zapisuj właściwy typ
|
|
Soneta (`Currency`, `DoubleCy`, `Amount`, `Percent`, `Date`), nie surowy `decimal`/`double`/`string`.
|
|
- Cechy **algorytmiczne**: przypisanie wartości uruchamia algorytm definicji — efekty uboczne; część
|
|
cech bywa read-only (`IsReadOnly(fd)` / tryb `SpecialEdit`) i edycja rzuci `AccessDeniedException`.
|
|
- W form.xml cechę adresuje się ścieżką `Features.Nazwa` (np. `{Features.NrSerii}`), także przez
|
|
relację (`{Kontrahent.Features.Segment}`).
|
|
- `dok.Pozycje` to kolekcja pozycji dokumentu — iteruj po niej, nie ładuj całej tabeli
|
|
`PozycjeDokHan`.
|
|
|
|
---
|
|
|
|
### HANDEL-W42 — Filtrowanie / wyszukiwanie dokumentów i partii po wartości cechy (serwerowo)
|
|
|
|
**Cel:** znaleźć dokumenty, pozycje, towary lub partie spełniające warunek na wartości cechy — z
|
|
filtrowaniem wykonywanym **po stronie SQL**, bez ładowania całej tabeli do pamięci.
|
|
|
|
**Warianty:**
|
|
|
|
| Wariant | Konstrukcja warunku | Uwaga |
|
|
|---|---|---|
|
|
| Równość wartości cechy | `new FieldCondition.Equal("Features.Nazwa", wartość)` | string-path, bo `Features.X` nie jest typowaną property |
|
|
| Większy / mniejszy | `FieldCondition.GreaterEqual / LessEqual("Features.Nazwa", v)` | dla cech liczbowych/dat |
|
|
| Łączenie warunków | `new RowCondition.And(...)` / `RowCondition.Or(...)` | składanie warunków serwerowych |
|
|
| Na indeksie tabeli | `tabela.WgKlucz[condition]` | filtr aplikowany na indeksie (SQL) |
|
|
| Na kolekcji `SubTable` | `dok.Pozycje[condition]` | filtr na pozycjach dokumentu |
|
|
| W widoku (UI) | `view.Condition &= new FieldCondition.Equal("Features.Nazwa", v)` | tylko kod UI / ViewInfo |
|
|
|
|
**Pola i typy:**
|
|
- `Soneta.Business.FieldCondition.Equal/GreaterEqual/LessEqual/...(string path, object value)` —
|
|
ścieżka cechy to literał `"Features.NazwaDefinicji"`.
|
|
- `Soneta.Business.RowCondition.And` / `RowCondition.Or` — kompozycja warunków.
|
|
- Indeksy do filtrowania: `handel.DokHandlowe.WgDaty[condition]` (dokumenty),
|
|
`towary.Towary.WgKodu[condition]` (towary), `magazyny.GrupyDostaw[...]` (partie).
|
|
|
|
**Snippet:**
|
|
|
|
```csharp
|
|
// 1) Towary po wartości cechy "Dystrybutor" = "Abc" (filtr serwerowy na indeksie)
|
|
var towary = session.GetTowary().Towary;
|
|
foreach (Towar t in towary.WgKodu[new FieldCondition.Equal("Features.Dystrybutor", "Abc")])
|
|
{
|
|
// ... tylko towary o tej cesze; SQL filtruje po DataKey cechy
|
|
}
|
|
|
|
// 2) Dokumenty handlowe oznaczone cechą "Pilne" = true
|
|
var handel = session.GetHandel();
|
|
foreach (DokumentHandlowy d in
|
|
handel.DokHandlowe.WgDaty[new FieldCondition.Equal("Features.Pilne", true)])
|
|
{
|
|
// ...
|
|
}
|
|
|
|
// 3) Złożony warunek: cecha LUB cecha (OR) — wszystkie indeksowane serwerowo
|
|
var orWarunek = new RowCondition.Or(
|
|
new FieldCondition.Equal("Features.Dystrybutor", "Abc"),
|
|
new FieldCondition.Equal("Features.Dystrybutor", "Cba"));
|
|
var wybrane = towary.WgKodu[orWarunek].ToArray();
|
|
|
|
// 4) Filtr po cesze + zakres (np. cecha-data dostawy >= dziś) na dokumentach
|
|
var pilneNaDzis = new RowCondition.And(
|
|
new FieldCondition.Equal("Features.Pilne", true),
|
|
new FieldCondition.GreaterEqual("Features.DataDostawy", Date.Today));
|
|
foreach (DokumentHandlowy d in handel.DokHandlowe.WgDaty[pilneNaDzis]) { /* ... */ }
|
|
|
|
// 5) Pozycje konkretnego dokumentu po cesze (filtr na kolekcji SubTable)
|
|
foreach (PozycjaDokHandlowego p in
|
|
dok.Pozycje[new FieldCondition.Equal("Features.NrSerii", "S-2026-001")])
|
|
{
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**Pułapki:**
|
|
- Cechy adresuj **string-pathem** `"Features.Nazwa"` w `FieldCondition` — `Features.X` nie jest
|
|
typowaną property `Row`, więc nie da się jej użyć w wyrażeniu LINQ (`(Row r) => r.Features…`).
|
|
- Warunek aplikuj **na indeksie** (`WgKodu[...]`, `WgDaty[...]`) lub na kolekcji `SubTable`
|
|
(`dok.Pozycje[...]`) — to wykonuje filtr w SQL. Nie iteruj całej tabeli z `if` w pamięci
|
|
(`safe-code.md` §6).
|
|
- Wyszukiwanie korzysta z indeksowanego pola `DataKey` cechy; wartość w warunku podawaj w typie
|
|
zgodnym z typem cechy (np. `bool` dla cechy Bool, `Date` dla cechy Date) — wartości są zapisane w
|
|
ustalonym formacie tekstowym (patrz tabela typów w `references/features.md`).
|
|
- `view.Condition &= …` to mechanizm **UI** (ViewInfo/folder); w kodzie biznesowym używaj
|
|
`SubTable[condition]`, nie obiektu `View`.
|
|
- `DokHandlowe` to tabela operacyjna guided — przy szerokich przekrojach dodatkowo zawężaj zakres
|
|
czasowy (data dokumentu), nie tylko warunek na cesze.
|
|
|
|
---
|
|
|