Files
soneta-erp-skills/soneta-programming/references/domeny/handel/HANDEL07-cechy.md
T

13 KiB

HANDEL07 — Cechy (Features)

Wspólne fakty o typie, podstawowe typy i szablon wzorca: ../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:

// 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:

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:

// 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 FieldConditionFeatures.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.