13 KiB
HANDEL01 — Fundamenty i identyfikacja
Wspólne fakty o typie, podstawowe typy i szablon wzorca: ../handel.md.
Rozdział opisuje, jak z poziomu sesji dotrzeć do modułów handlowo-magazynowych, jak poprawnie wskazać definicję dokumentu (
DefDokHandlowego) zanim utworzysz dokument, oraz jak na podstawie definicji i flag dokumentu rozpoznać jego rodzaj (faktura / magazynowy / zamówienie / korekta / zaliczka). Cały kod jest zgodny z C# 10 i operuje wyłącznie na publicznym kontrakcie platformy. Fundamenty wspólne (sesja, transakcjasession.Logout(true)+Commit/CommitUI, blokada optymistyczna, praca zSubTable) opisująsafe-code.md,session-login.mdorazworker-extender.md— tutaj się do nich odwołujemy, nie powtarzamy ich.
HANDEL-W1 — Dostęp do modułów handlowo-magazynowych i tabeli DokHandlowe
Cel: z obiektu Session (lub dowolnego ISessionable — Row, Table, Context) dotrzeć do
modułów, na których opiera się logika handlu i magazynu, oraz do tabeli dokumentów DokHandlowe.
To punkt wejścia każdego scenariusza w tym dokumencie.
Warianty:
| Wariant | Wywołanie (extension method na Session) |
Co udostępnia |
|---|---|---|
| Moduł handlowy | session.GetHandel() → HandelModule |
.DokHandlowe (tabela dokumentów), .DefDokHandlowych (definicje) |
| Moduł magazynowy | session.GetMagazyny() → MagazynyModule |
.Magazyny, .Zasoby, .Obroty, .GrupyDostaw (partie), .OkresyMag |
| Moduł towarów | session.GetTowary() → TowaryModule |
.Towary, .Jednostki |
| Moduł CRM | session.GetCRM() → CRMModule |
.Kontrahenci |
| Moduł kasowy | session.GetKasa() → KasaModule |
formy płatności, rozrachunki (dot. płatności dokumentu) |
| Waluty | Soneta.Waluty.WalutyModule.GetInstance(session) |
.Waluty, .TabeleKursowe |
Pola i typy: HandelModule.DokHandlowe: DokHandlowe (tabela DokumentHandlowy),
HandelModule.DefDokHandlowych (tabela DefDokHandlowego),
MagazynyModule.Magazyny, TowaryModule.Towary, CRMModule.Kontrahenci. Wszystkie moduły
implementują ISessionable i mają property .Session.
Snippet:
// Punkt wejścia — z sesji pobieramy moduły handlowo-magazynowe:
var handel = session.GetHandel(); // HandelModule
var magazyny = session.GetMagazyny(); // MagazynyModule
var towary = session.GetTowary(); // TowaryModule
var crm = session.GetCRM(); // CRMModule
// Tabela dokumentów handlowych (operacyjna, guided):
var dokumenty = handel.DokHandlowe;
// Iteracja po dokumentach — ZAWSZE zawężaj zakres (data/definicja/kontrahent),
// to tabela operacyjna rosnąca z biznesem. Filtr aplikujemy na indeksie (warunek serwerowy):
var od = Date.Today.AddMonths(-1);
foreach (DokumentHandlowy d in handel.DokHandlowe.WgDaty[(DokumentHandlowy x) => x.Data >= od])
{
// d.* — Numer, Data, Definicja, Kontrahent, Suma, Stan ...
}
// Z dowolnego ISessionable można zejść do modułu również metodą GetInstance:
var hm = Soneta.Handel.HandelModule.GetInstance(jakisRow); // gdy nie mamy zmiennej Session
Pułapki:
- Moduł i tabela są single-threaded — nie współdziel ich między wątkami; pobieraj je z sesji bieżącego wątku (thread-safety w SKILL.md).
session.GetWaluty()jest internal — z dodatku zewnętrznego użyjSoneta.Waluty.WalutyModule.GetInstance(session).- Nie ładuj całej tabeli
DokHandlowedo pamięci zif-em w pętli. Filtruj serwerowo — warunek aplikuj na indeksie tabeli (np.WgDaty[(DokumentHandlowy x) => …]), żeby wykonał się po stronie SQL (safe-code §6). W warunkuRowConditionużywaj tylko pól bazodanowych — pola kalkulowane rzucąLinqConditionException. - Pobranie modułu nie tworzy ani nie modyfikuje danych — modyfikacje zawsze w transakcji
(
session.Logout(true)+Commit/CommitUI, potemSave).
HANDEL-W2 — Wybór definicji dokumentu (DefDokHandlowego) wg symbolu
Cel: zanim utworzysz dokument, musisz wskazać jego definicję — to ona określa typ dokumentu
(sprzedaż, zakup, magazynowy, zamówienie…), numerację, zachowanie magazynu i płatności. Definicja
jest pierwszym ustawianym polem nowego dokumentu (dok.Definicja = …), zanim ustawisz magazyn,
kontrahenta czy pozycje.
Warianty:
| Wariant | Klucz / mechanizm | Uwaga |
|---|---|---|
| Po symbolu | DefDokHandlowych.WgSymbolu["FV"] |
indeks unikalny — zwraca pojedynczy rekord lub null |
| Filtr po kategorii (typie) | DefDokHandlowych.WgKategorii[KategoriaHandlowa.Sprzedaż] |
zbiór wszystkich definicji danej kategorii |
| Po symbolu w obrębie kategorii | warunek serwerowy na WgSymbolu + sprawdzenie Kategoria |
gdy w bazie istnieje kilka wariantów sprzedaży |
| Walidacja istnienia | WgSymbolu[symbol] != null |
brak definicji = nie da się utworzyć dokumentu |
Typowe symbole w bazie Demo: FV (faktura sprzedaży), FZ (faktura zakupu), PAR (paragon),
PZ/PW (przyjęcia magazynowe), WZ/RW (rozchody magazynowe), ZO (zamówienie
odbiorcy), ZD (zamówienie do dostawcy), MM (przesunięcie międzymagazynowe),
INW (inwentaryzacja), KS (korekta sprzedaży). Symbole zależą od konfiguracji konkretnej bazy —
nie zakładaj ich „na sztywno", weryfikuj != null.
Pola i typy: DefDokHandlowego.Symbol: string (maks. 12 znaków, unikalny),
DefDokHandlowego.Kategoria: Soneta.Handel.KategoriaHandlowa. Indeks WgSymbolu jest unikalny
(zwraca pojedynczy rekord), WgKategorii grupuje definicje po kategorii.
Snippet:
var handel = session.GetHandel();
// 1. Po symbolu — klucz unikalny: pojedynczy rekord albo null
DefDokHandlowego defFV = handel.DefDokHandlowych.WgSymbolu["FV"];
if (defFV == null)
throw new BusException("Brak definicji dokumentu o symbolu FV w tej bazie.".Translate());
// 2. Wszystkie definicje danej kategorii (np. wszystkie definicje sprzedaży):
foreach (DefDokHandlowego d in handel.DefDokHandlowych.WgKategorii[KategoriaHandlowa.Sprzedaż])
{
// d.Symbol, d.Kategoria ...
}
// 3. Użycie definicji przy tworzeniu dokumentu — Definicja USTAWIANA PIERWSZA:
using (var t = session.Logout(editMode: true))
{
var dok = new DokumentHandlowy();
session.AddRow(dok); // AddRow przed ustawianiem pól
dok.Definicja = handel.DefDokHandlowych.WgSymbolu["PW"]; // definicja jako pierwsze pole
// dok.Magazyn / dok.Kontrahent ustawiamy dopiero PO definicji (gdy definicja ich wymaga)
t.Commit(); // CommitUI() w workerze/extenderze
}
session.Save();
Pułapki:
WgSymbolu[...]zwraca pojedynczy rekord (klucz unikalny) i może byćnull— zawsze sprawdź przed użyciem.WgKategorii[...]zwraca zbiór — iteruj lub.FirstOrDefault().- Definicja musi być ustawiona jako pierwsze pole dokumentu — od niej zależy widoczność i wymagalność pozostałych pól (magazyn, kontrahent, numeracja). Ustawienie magazynu/kontrahenta przed definicją jest błędem.
- Symbole nie są gwarantowane — zależą od konfiguracji bazy klienta. Nie polegaj na obecności
„FV"/„WZ"; pobierz definicję i sprawdź
!= null, a w razie potrzeby filtruj poKategoria. DefDokHandlowegoto dane konfiguracyjne (GuidedRow) — odczytuj je, nie twórz „w locie" w kodzie operacyjnym.
HANDEL-W3 — Rozpoznanie rodzaju dokumentu (faktura / magazynowy / zamówienie / korekta / zaliczka)
Cel: ustalić, „czym jest" dany dokument — fakturą, dokumentem magazynowym, zamówieniem, korektą
czy dokumentem zaliczkowym — by rozgałęzić logikę (np. inaczej traktować rozchód magazynowy niż
zamówienie). Rozpoznanie opiera się na kategorii definicji (Definicja.Kategoria) oraz na
gotowych flagach dokumentu (Korekta, JestDokZaliczkowy()).
Warianty:
| Co rozpoznajemy | Mechanizm (publiczny kontrakt) | Wartości / zakres KategoriaHandlowa |
|---|---|---|
| Faktura/handlowy (sprzedaż, zakup, korekty, f. wewnętrzna) | Definicja.Kategoria w zakresie handlowym |
Sprzedaż=2, KorektaSprzedaży=3, Zakup=4, KorektaZakupu=5, FakturaWewnętrzna=6 (zakres HandelPierwszy=1 … HandelOstatni=100) |
| Magazynowy (PW/PZ/WZ/RW/MM/INW…) | Definicja.Kategoria w zakresie magazynowym |
PrzyjęcieMagazynowe=102, WydanieMagazynowe=104, PrzesunięcieMagazynowe=106, Inwentaryzacja=107 … (zakres MagazynPierwszy=101 … MagazynOstatni=200) |
| Zamówienie (ZO/ZD/wewn.) | Definicja.Kategoria |
ZamówienieOdbiorcy=302, ZamówienieDostawcy=303, ZamówienieWewnętrzne=312 |
| Korekta | flaga dok.Korekta lub kategoria typu Korekta* |
dok.Korekta == true; kategorie: KorektaSprzedaży, KorektaZakupu, KorektaPrzyjęciaMagazynowego, KorektaWydaniaMagazynowego … |
| Dokument zaliczkowy | metoda dok.JestDokZaliczkowy() / dok.JestDokZaliczkowy(out bool korekta) |
true = zaliczkowy; out korekta = korekta zaliczki |
Pola i typy:
DokumentHandlowy.Definicja: Soneta.Handel.DefDokHandlowego— definicja dokumentu.DefDokHandlowego.Kategoria: Soneta.Handel.KategoriaHandlowa— kluczowy wyznacznik rodzaju.DokumentHandlowy.Korekta: bool(kalkulowane, read-only) — czy dokument jest korektą.DokumentHandlowy.JestDokZaliczkowy(): boolorazJestDokZaliczkowy(out bool korekta): bool— rozpoznanie zaliczki (drugi przeciążony wariant zwraca też, czy to korekta zaliczki).DefDokHandlowego.Symbol: string— symbol (do logów / komunikatów).
Enum Soneta.Handel.KategoriaHandlowa (wartości publiczne) ma czytelne markery zakresów:
HandelPierwszy=1/HandelOstatni=100, MagazynPierwszy=101/MagazynOstatni=200,
PozostałePierwszy=301/PozostałeOstatni=400. Pozwalają one rozpoznać „grupę" dokumentu zakresem,
bez wyliczania wszystkich symboli.
Snippet:
// Rozpoznanie rodzaju dokumentu na podstawie kategorii jego definicji + flag dokumentu.
// KategoriaHandlowa to enum — markery zakresów (HandelPierwszy/Ostatni, MagazynPierwszy/Ostatni)
// pozwalają klasyfikować grupę dokumentu bez wymieniania wszystkich symboli.
static string RozpoznajRodzaj(DokumentHandlowy dok)
{
KategoriaHandlowa kat = dok.Definicja.Kategoria;
// Zaliczka i korekta mają dedykowane, jednoznaczne testy — sprawdzamy je najpierw:
if (dok.JestDokZaliczkowy(out bool korektaZaliczki))
return korektaZaliczki ? "Korekta zaliczki" : "Dokument zaliczkowy";
if (dok.Korekta)
return "Korekta";
// Klasyfikacja grupy po zakresie wartości enuma (markery są publiczne):
return kat switch
{
>= KategoriaHandlowa.HandelPierwszy and <= KategoriaHandlowa.HandelOstatni => "Faktura / dokument handlowy",
>= KategoriaHandlowa.MagazynPierwszy and <= KategoriaHandlowa.MagazynOstatni => "Dokument magazynowy",
KategoriaHandlowa.ZamówienieOdbiorcy
or KategoriaHandlowa.ZamówienieDostawcy
or KategoriaHandlowa.ZamówienieWewnętrzne => "Zamówienie",
_ => "Inny"
};
}
// Przykład użycia — rozgałęzienie logiki po rodzaju:
DokumentHandlowy dok = session.GetHandel().DokHandlowe.WgDaty[
(DokumentHandlowy d) => d.Data == Date.Today].FirstOrDefault();
if (dok != null && dok.Definicja.Kategoria == KategoriaHandlowa.WydanieMagazynowe)
{
// ... logika dotycząca rozchodu magazynowego
}
Pułapki:
- Rodzaj wynika z definicji, nie z symbolu. Symbol (np. „FV") jest dowolny i zależny od bazy —
rozpoznawaj po
Definicja.Kategoria, a nie po porównaniuSymbol == "FV". - Pomocnicze metody rozszerzające na enumie (
JestHandlowa,JestMagazynowa,JestZamowienie) sąinternal— z dodatku zewnętrznego ich nie wywołasz. Klasyfikuj zakresami markerów (>= HandelPierwszy and <= HandelOstatniitd.) lub porównaniem do konkretnych wartości — tak jak w snippetcie. - Wartości
*Pierwszy/*Ostatnisą oznaczone[Hidden](nie pokazują się w UI), ale to publiczne stałe enuma — wolno ich użyć w kodzie jako granic zakresu. Korektai wynikiJestDokZaliczkowy()są kalkulowane (read-only) — służą tylko do odczytu; nie próbuj ich ustawiać. Korektę tworzy się przez relacje dokumentów (IRelacjeService.NowaKorekta), a nie przez przestawienie flagi.- Sprawdzaj zaliczkę/korektę przed klasyfikacją zakresową: korekta sprzedaży nadal mieści się w
zakresie handlowym, a zaliczka bywa fakturą — dedykowane testy (
JestDokZaliczkowy,Korekta) są bardziej szczegółowe i powinny mieć pierwszeństwo. dok.Definicjamoże w teorii byćnullna świeżo utworzonym, jeszcze nieskonfigurowanym dokumencie — przy klasyfikacji dokumentów „w trakcie tworzenia" zabezpiecz dostęp doKategoria.