SKILL: Uporządkowanie skills domenowych - podział na mniejsze pliki i wspólna numeracja

This commit is contained in:
Marcin Wojas
2026-06-07 08:31:37 +02:00
parent fb2f2695a3
commit 5605ad2915
42 changed files with 13500 additions and 13526 deletions
@@ -0,0 +1,326 @@
# HANDEL03 — Stany dokumentu i cykl życia
> Wspólne fakty o typie, podstawowe typy i szablon wzorca: [../handel.md](../handel.md).
Stan dokumentu handlowego steruje całym jego cyklem życia: od bufora (rekord roboczy, swobodnie
edytowalny i usuwalny), przez zatwierdzenie (księgowanie obrotów magazynowych, generowanie
płatności, blokada większości pól), aż po anulowanie. Stanem steruje **jedno zapisywalne pole**
`dok.Stan`, a dodatkowe operacje serwisowe (naprawa, przeliczenie) wykonują publiczne workery.
> **Fundamenty** (sesja, transakcja edycyjna `session.Logout(editMode: true)`, `Commit`/`CommitUI`,
> blokada optymistyczna w `Save()`) opisuje [`safe-code.md`](../safe-code.md) — tu się do nich
> odwołujemy, nie powtarzamy. Cały kod jest zgodny z **C# 10** i operuje wyłącznie na **publicznym
> kontrakcie** platformy.
**Fakty o stanie (zweryfikowane):**
- **Pole sterujące:** `dok.Stan: Soneta.Handel.StanDokumentuHandlowego` (zapisywalne w transakcji).
- **Enum `StanDokumentuHandlowego`:** `Bufor=0`, `Zatwierdzony=1`, `Zablokowany=2`, `Anulowany=3`.
Wartość `Zablokowany` ustawia **platforma** (np. po zaksięgowaniu w ewidencji) — nie ustawiaj jej z
dodatku „z palca".
- **Skróty kalkulowane (tylko do odczytu, `bool`):** `dok.Bufor`, `dok.Zatwierdzony`, `dok.Anulowany`.
- **Usunięcie z bufora:** `dok.Delete()` w transakcji (tylko gdy brak zależności).
- **Workery publiczne (cykl życia / naprawa):** `Soneta.Handel.PoprawaStanuDokumentuWorker`,
`Soneta.Magazyny.PrzeliczenieStanuWorker`.
---
### HANDEL-W12 — Zatwierdzenie dokumentu (bufor → zatwierdzony)
**Cel:** przeprowadzić dokument z bufora do stanu zatwierdzonego. Dopiero zatwierdzenie + `Save()`
księguje obroty magazynowe, tworzy zasoby/partie, generuje płatności i czyni dokument nadrzędnym dla
relacji (np. ZO→FV, FA→WZ — patrz rozdział o relacjach).
**Warianty:**
| Wariant | Operacja | Uwaga |
|---|---|---|
| Zatwierdzenie pojedyncze | `dok.Stan = StanDokumentuHandlowego.Zatwierdzony` | w transakcji + `Save()` |
| Zatwierdzenie zbiorcze | worker `EwidencjonowanieZbiorczeWorker` (`[Context] DokumentHandlowy[]`) | wiele dokumentów naraz |
| Sprawdzenie stanu | `dok.Zatwierdzony` / `dok.Bufor` (kalkulowane `bool`) | bez porównywania enuma |
| Stan `Zablokowany` | ustawiany przez platformę (księgowanie ewidencji) | nie ustawiaj ręcznie |
**Pola i typy:** `dok.Stan: StanDokumentuHandlowego` (zapisywalne), `dok.Bufor/Zatwierdzony/Anulowany:
bool` (kalkulowane). Wartości magazynowe widoczne **po** `Save()`: `dok.Zasoby`, `dok.Obroty`,
`dok.SumyVAT`.
**Snippet:**
```csharp
var hm = session.GetHandel();
var dok = hm.DokHandlowe.WgDaty[/* ... */]; // odczytany dokument w buforze
using (var t = session.Logout(editMode: true))
{
dok.Stan = StanDokumentuHandlowego.Zatwierdzony; // bufor -> zatwierdzony
t.Commit(); // CommitUI() w workerze/extenderze
}
session.Save(); // DOPIERO TERAZ księgowane są obroty/zasoby/płatności
// Sprawdzenie po zapisie — czytaj pola kalkulowane, nie porównuj enuma:
if (dok.Zatwierdzony)
{
foreach (var z in dok.Zasoby) { /* zasoby utworzone przez dokument przychodowy */ }
}
```
**Pułapki:**
- **Magazyn księguje się dopiero po `Save()`** — samo `Commit()`/`CommitUI()` nie tworzy obrotów ani
zasobów. Jeśli baza blokuje stan ujemny (weryfikator `StanUjemnyVerifier`, jak w bazie Demo),
rozchód (FV/WZ/RW) wymaga **wcześniej zapisanego** przyjęcia (PW/PZ) tego towaru — inaczej `Save()`
rzuci wyjątek.
- Zatwierdzenie uruchamia walidatory dokumentu (kompletność pozycji, magazyn, kontrahent, tabela
VAT). Błędy wychodzą w `Commit()`/`Save()` jako `RowException` — nie połykaj ich (safe-code §4).
- W workerze/extenderze użyj `t.CommitUI()` zamiast `t.Commit()`
([`worker-extender.md`](../worker-extender.md)).
- Po `Save()` w środku jednej sesji zamyka się okno edycji; kolejna edycja na **tym samym** obiekcie
bez ponownego `Logout` rzuci `AccessWriteDenied`. Wzorzec: zapis → odczyt na świeżej sesji.
- Nie ustawiaj `Stan = Zablokowany` z dodatku — to stan wewnętrzny platformy (np. po zaksięgowaniu w
ewidencji).
---
### HANDEL-W13 — Cofnięcie do bufora / odtwierdzenie
**Cel:** wycofać zatwierdzony dokument z powrotem do bufora, aby go poprawić. Operacja odksięgowuje
to, co zatwierdzenie zaksięgowało (obroty, płatności), więc jest dozwolona **tylko** gdy nie ma
zależności blokujących (zamknięty okres magazynowy/VAT, zaksięgowanie w ewidencji, dokumenty
podrzędne).
**Warianty:**
| Wariant | Operacja | Warunek dozwolenia |
|---|---|---|
| Cofnięcie do bufora | `dok.Stan = StanDokumentuHandlowego.Bufor` | okres otwarty, brak podrzędnych, nie zaksięgowany |
| Dokument zablokowany | najpierw zdjąć blokadę po stronie ewidencji/księgowości | `dok.Stan == Zablokowany` blokuje cofnięcie |
| Z dokumentami podrzędnymi | najpierw usuń/rozłącz podrzędne (relacje) | patrz rozdział o relacjach i HANDEL-W16 |
**Pola i typy:** `dok.Stan: StanDokumentuHandlowego`, `dok.Zatwierdzony/Bufor: bool` (kalkulowane),
`dok.DokumentyMagazynowe`, `dok.DokumentyHandlowe`, `dok.DokumentyKorygujące` (kalkulowane — do
sprawdzenia zależności przed cofnięciem).
**Snippet:**
```csharp
var dok = session.GetHandel().DokHandlowe.WgDaty[/* ... */];
if (!dok.Zatwierdzony) return; // już w buforze / anulowany — nic do zrobienia
// Cofnięcie jest zablokowane, gdy istnieją dokumenty podrzędne (korekty, magazynowe):
bool maZaleznosci = dok.DokumentyKorygujące.Any() || dok.DokumentyMagazynowe.Length > 0;
if (maZaleznosci)
throw new BusException(
"Nie można cofnąć dokumentu do bufora — istnieją powiązane dokumenty.".Translate());
using (var t = session.Logout(editMode: true))
{
dok.Stan = StanDokumentuHandlowego.Bufor; // odtwierdzenie: zatwierdzony -> bufor
t.Commit(); // CommitUI() w workerze/extenderze
}
session.Save(); // tu odksięgowanie obrotów/płatności i wykrycie konfliktów
```
**Pułapki:**
- Cofnięcie dokumentu w **zamkniętym okresie** magazynowym/VAT albo zaksięgowanego w ewidencji
zakończy się wyjątkiem w `Commit()`/`Save()`. Sprawdź stan otwarcia okresu zanim spróbujesz.
- Dokument w stanie `Zablokowany` nie cofniesz przez `dok.Stan = Bufor` — blokada wynika z innego
modułu (np. ewidencja zaksięgowana). Do diagnozy/naprawy rozbieżności stanu dokument↔ewidencja służy
`PoprawaStanuDokumentuWorker` (HANDEL-W15).
- Jeśli istnieją dokumenty podrzędne (korekty, powiązane magazynowe), cofnięcie się nie powiedzie —
najpierw rozwiąż powiązania (rozdział o relacjach), patrz też HANDEL-W16.
- To **nie** to samo co anulowanie (HANDEL-W14): cofnięcie wraca do edytowalnego bufora, anulowanie zamyka
dokument w stanie nieodwracalnym.
---
### HANDEL-W14 — Anulowanie dokumentów
**Cel:** unieważnić dokument, który nie powinien już brać udziału w obrocie (np. wystawiony omyłkowo),
zachowując go w bazie dla ciągłości numeracji i audytu. Anulowanie odksięgowuje skutki magazynowe i
finansowe, ale rekord pozostaje (w przeciwieństwie do `Delete()`).
**Warianty:**
| Wariant | Operacja | Uwaga |
|---|---|---|
| Anulowanie z bufora | `dok.Stan = StanDokumentuHandlowego.Anulowany` | bufor → anulowany |
| Anulowanie zatwierdzonego | `dok.Stan = StanDokumentuHandlowego.Anulowany` | odksięgowuje obroty/płatności; tylko gdy okres otwarty |
| Sprawdzenie | `dok.Anulowany` (kalkulowane `bool`) | bez porównywania enuma |
**Pola i typy:** `dok.Stan: StanDokumentuHandlowego`, `dok.Anulowany: bool` (kalkulowane).
**Snippet:**
```csharp
var dok = session.GetHandel().DokHandlowe.WgDaty[/* ... */];
if (dok.Anulowany) return; // już anulowany
using (var t = session.Logout(editMode: true))
{
dok.Stan = StanDokumentuHandlowego.Anulowany; // bufor lub zatwierdzony -> anulowany
t.Commit(); // CommitUI() w workerze/extenderze
}
session.Save();
// Po anulowaniu dokument pozostaje w bazie (numeracja zachowana), ale nie wpływa na stany:
bool wycofany = dok.Anulowany;
```
**Pułapki:**
- Anulowanie zatwierdzonego dokumentu odksięgowuje jego skutki — w **zamkniętym okresie** albo gdy
istnieją dokumenty podrzędne kończy się wyjątkiem. Najpierw rozwiąż zależności (jak w HANDEL-W13).
- Anulowanie jest **nieodwracalne** — nie ma przejścia `Anulowany → Bufor` na poziomie pola `Stan`.
Gdy chcesz tylko poprawić dokument, użyj cofnięcia do bufora (HANDEL-W13).
- Anulowany dokument zwykle nie powinien być źródłem relacji ani korekt — generowanie podrzędnych z
anulowanego nadrzędnego zostanie odrzucone.
- Do trwałego usunięcia rekordu (gdy dozwolone) służy `Delete()` (HANDEL-W16), a nie anulowanie —
anulowanie zachowuje rekord i numer.
---
### HANDEL-W15 — Naprawa i przeliczenie stanu dokumentu
**Cel:** naprawić rozbieżności między dokumentem a jego skutkami: stan dokumentu vs stan dokumentu
ewidencji (`PoprawaStanuDokumentuWorker`) oraz zgodność obrotów/zasobów magazynowych z pozycjami
(`PrzeliczenieStanuWorker`). To operacje serwisowe — uruchamiaj świadomie, nie w pętli zwykłej
logiki.
**Warianty:**
| Wariant | Worker (publiczny) | Akcja menu / wejście |
|---|---|---|
| Naprawa stanu dokumentu (synchron. z ewidencją) | `Soneta.Handel.PoprawaStanuDokumentuWorker` | „Narzędziowe/Naprawa stanu dokumentu"; `[Context] Dokument` |
| Sprawdzenie poprawności obrotów (bez zapisu) | `Soneta.Magazyny.PrzeliczenieStanuWorker`, `Opcje.SprawdzićPoprawność` | „Narzędziowe/Naliczenie obrotów towaru" |
| Ponowne pełne przeliczenie | `PrzeliczenieStanuWorker`, `Opcje.PonowniePrzeliczyć` | jw. (zapis w transakcji) |
| Poprawa tylko błędnych | `PrzeliczenieStanuWorker`, `Opcje.PoprawićTylkoBłędne` | jw. |
| Poprawa / sprawdzenie samych obrotów | `Opcje.PoprawićObroty` / `Opcje.SprawdzićObroty` | jw. |
**Pola i typy (publiczny kontrakt workerów):**
- `PoprawaStanuDokumentuWorker`: property `[Context] public DokumentHandlowy Dokument`; akcja
`public void NaprawStan()`; predykat widoczności
`public static bool IsVisibleNaprawStan(DokumentHandlowy dokument)`. Worker sam zarządza
transakcją wewnątrz `NaprawStan()` (synchronizuje `dok.Stan` z dokumentem ewidencji, w razie potrzeby
tworzy/kasuje ewidencję, może przestawić `Stan` na `Zablokowany`/`Zatwierdzony`).
- `PrzeliczenieStanuWorker`: enum `public enum Opcje { SprawdzićPoprawność, PoprawićTylkoBłędne,
PrzeliczyćTylkoNiepoprawione, PonowniePrzeliczyć, PoprawićObroty, SprawdzićObroty }`; konstruktor
publiczny `PrzeliczenieStanuWorker(Opcje wykonaj, bool wszystkieMagazyny, bool rozchód0, bool
przywracajWartość)`; property `[Context]` `Dokument`, `Towar`, `Magazyny` (`Magazyn[]`); akcja
`public void PrzeliczStan()`. Worker sam otwiera transakcje wewnątrz `PrzeliczStan()`.
**Snippet:**
```csharp
var dok = session.GetHandel().DokHandlowe.WgDaty[/* ... */];
// 1. Naprawa rozbieżności stanu dokumentu względem dokumentu ewidencji.
// Worker sam prowadzi transakcje — ustaw tylko kontekst i wywołaj akcję.
var naprawa = new PoprawaStanuDokumentuWorker { Dokument = dok };
naprawa.NaprawStan();
session.Save(); // utrwalenie zmian dokonanych przez workera
// 2. Sprawdzenie poprawności obrotów dokumentu BEZ wprowadzania zmian (tryb diagnostyczny):
var sprawdz = new PrzeliczenieStanuWorker(
PrzeliczenieStanuWorker.Opcje.SprawdzićPoprawność,
wszystkieMagazyny: false, rozchód0: false, przywracajWartość: true) { Dokument = dok };
sprawdz.PrzeliczStan(); // tryb SprawdzićPoprawność nie commituje — tylko raportuje (Trace)
// 3. Pełne ponowne przeliczenie obrotów dokumentu (modyfikuje dane):
var przelicz = new PrzeliczenieStanuWorker(
PrzeliczenieStanuWorker.Opcje.PoprawićTylkoBłędne,
wszystkieMagazyny: false, rozchód0: false, przywracajWartość: true) { Dokument = dok };
przelicz.PrzeliczStan();
session.Save();
```
**Pułapki:**
- Oba workery **same zarządzają transakcjami** wewnątrz swoich akcji (`NaprawStan`/`PrzeliczStan`).
Nie owijaj wywołania własnym `session.Logout(true)` — wystarczy `session.Save()` po akcji, by
utrwalić zmiany.
- W realnej aplikacji akcje są rejestrowane z `Mode = ActionMode.IsolatedSession | Progress`, czyli
uruchamiają się w **izolowanej sesji**. Przy programowym wywołaniu działasz na bieżącej sesji —
upewnij się, że nie koliduje to z innymi otwartymi transakcjami.
- `Opcje.SprawdzićPoprawność` to tryb **tylko diagnostyczny** — nie zmienia danych, raportuje przez
`Trace`. Do faktycznej naprawy użyj `PoprawićTylkoBłędne`/`PonowniePrzeliczyć`.
- `PrzeliczenieStanuWorker` rzuca `RowException`, gdy napotka obrót w **zamkniętym okresie**
magazynowym albo dokument korygowany w buforze („Dokument korygowany … w buforze. Należy go
zatwierdzić.") — obsłuż te przypadki, nie wywołuj przeliczenia na ślepo.
- `PoprawaStanuDokumentuWorker.IsVisibleNaprawStan` zwraca `false` dla dokumentów z obsługą
technologii produkcji i magazynu pozabilansowego — to sygnał, że dla takich dokumentów naprawa nie
ma zastosowania.
- To są narzędzia serwisowe — nie używaj ich jako rutynowego elementu logiki tworzenia dokumentów.
---
### HANDEL-W16 — Bezpieczne usunięcie dokumentu z bufora i obsługa zależności
**Cel:** trwale usunąć dokument z bazy (`Delete()`), gdy jest błędny i jeszcze niepowiązany. Usuwanie
jest dozwolone **wyłącznie w buforze** i tylko gdy nie istnieją zależności (rezerwacje, dokumenty
magazynowe/handlowe powiązane, korekty). W przeciwnym razie świadomie odmów (lub anuluj — HANDEL-W14).
**Warianty:**
| Wariant | Sytuacja | Zalecenie |
|---|---|---|
| Usunięcie czyste | bufor, brak powiązań i rezerwacji | dozwolone (`dok.Delete()`) |
| Dokument zatwierdzony | poza buforem | najpierw cofnij do bufora (HANDEL-W13) lub anuluj (HANDEL-W14) |
| Z rezerwacją | `dok.Rezerwacja != null` | usuń/zwolnij rezerwację najpierw (relacje) |
| Z dokumentami powiązanymi | `DokumentyMagazynowe`/`DokumentyHandlowe`/korekty niepuste | rozłącz/usuń podrzędne lub anuluj |
**Pola i typy (do oceny zależności — kalkulowane, tylko odczyt):** `dok.Bufor: bool`,
`dok.Rezerwacja`, `dok.DokumentyMagazynowe: DokumentHandlowy[]`, `dok.DokumentyHandlowe:
DokumentHandlowy[]`, `dok.DokumentyKorygujące: IEnumerable`, `dok.DokumentKorygowany`,
`dok.DokumentyZaliczkowe`.
**Snippet:**
```csharp
var dok = session.GetHandel().DokHandlowe.WgDaty[/* ... */];
// 1. Usuwać można tylko z bufora:
if (!dok.Bufor)
throw new BusException(
"Usunąć można tylko dokument w buforze. Cofnij do bufora lub anuluj.".Translate());
// 2. Zależności blokujące usunięcie (rezerwacja, powiązane, korekty):
bool maZaleznosci =
dok.Rezerwacja != null ||
dok.DokumentyMagazynowe.Length > 0 ||
dok.DokumentyHandlowe.Length > 0 ||
dok.DokumentyKorygujące.Any();
if (maZaleznosci)
throw new BusException(
"Nie można usunąć dokumentu — istnieją powiązania (rezerwacja/dokumenty/korekty).".Translate());
using (var t = session.Logout(editMode: true))
{
dok.Delete(); // twarde usunięcie — tylko gdy bufor i brak zależności
t.Commit(); // CommitUI() w workerze/extenderze
}
session.Save(); // integralność weryfikowana także tutaj
```
**Pułapki:**
- Sprawdzaj zależności **przed** `Delete()`. Próba usunięcia powiązanego dokumentu i tak zostanie
odrzucona przez integralność (wyjątek w `Save()`), ale lepiej zdecydować świadomie i zwrócić czytelny
komunikat.
- Usunięcie usuwa też **pozycje** dokumentu — wykonuj je jedną transakcją; nie kasuj pozycji „ręcznie"
przed `dok.Delete()`, jeśli i tak usuwasz cały dokument.
- Gdy dokument jest **zatwierdzony**, najpierw cofnij go do bufora (HANDEL-W13). Jeśli cofnięcie jest
zablokowane (okres zamknięty, podrzędne), rozważ **anulowanie** (HANDEL-W14) zamiast usuwania — anulowanie
zachowuje numer i ścieżkę audytu.
- Rezerwacje rozwiązuje logika relacji/magazynu (workery rezerwacji są **internal** — z dodatku
operuj przez publiczne API relacji oraz pola `dok.Rezerwacja`), nie kasuj rekordów rezerwacji
bezpośrednio z dodatku.
- `Delete()` na dokumencie poza buforem (zatwierdzony/zablokowany/anulowany) jest zabronione — nie
obchodź tego przez bezpośrednie operacje na tabeli.
---