Files
soneta-erp-skills/soneta-programming/references/domeny/kadry/KADRY01-pracownik.md
T

1248 lines
61 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# KADRY01 — Pracownik — zatrudnienie i dane kartotekowe
> Wspólne fakty o typie, podstawowe typy i szablon wzorca: [../kadry.md](../kadry.md).
> **Model „root + historia".** `Pracownik` (root, tabela `Pracownicy`) trzyma tylko nieliczne pola
> niezmienne w czasie (`Kod`, `Net`, `NumerRachunkuUS`, `NumerRachunkuZUS`, `Rodzaj`, `Typ`,
> `Wieloetatowosc`). **Praktycznie wszystkie dane kadrowe są historyczne** i leżą w zapisach
> `PracHistoria` (tabela `PracHistorie`, child `Pracownik`-a, też `GuidedRow` root z własnym Guid).
> Kolekcją zapisów jest `Pracownik.Historia: HistorySubTable<Soneta.Kadry.PracHistoria>`. Warunki
> zatrudnienia (umowa, wymiar, ubezpieczenia, stanowisko) siedzą w złożonym polu
> `PracHistoria.Etat: Soneta.Kadry.Etat`.
>
> **Skróty na rootcie** (delegują do zapisu „na dziś"/ostatniego):
> - `pracownik.Last : PracHistoria` — **ostatni** (najświehigy) zapis historii; do edycji świeżo
> utworzonego pracownika i odczytu „bieżących" danych kartotekowych.
> - `pracownik[date] : PracHistoria` — indeksator zwracający zapis **obowiązujący na zadaną datę**.
> - Wiele pól osobowych jest też dostępnych z poziomu rootu jako property delegujące (np. `Imie`,
> `Nazwisko`, `PESEL`) — ale **kanonicznie ustawiamy je na zapisie** (`Last.Imie`, `pracownik[d].PESEL`).
>
> **`Pracownik` jest klasą abstrakcyjną** — nie da się zrobić `new Pracownik()`. Konkretny typ
> pracownika firmy to **`Soneta.Kadry.PracownikFirmy`**.
### KADRY-A1 — Zatrudnienie nowego pracownika (★)
**Cel:** utworzyć nowego pracownika z minimalnym kompletem danych pozwalającym na `Session.Save()`.
**Mechanizm (kluczowy):** dodanie nowego `PracownikFirmy` do tabeli (`AddRow`) automatycznie tworzy
**pierwszy zapis** `PracHistoria` oraz kalendarz pracownika (dzieje się w `OnAdded`). Dlatego **nie
tworzymy** `PracHistoria` ręcznie przy zatrudnieniu — bezpośrednio po `AddRow` istnieje już
`pracownik.Last` (pierwszy zapis), na którym ustawiamy dane osobowe.
**Pola minimalne do zapisu:**
| Pole | Gdzie | Typ | Uwaga |
|---|---|---|---|
| `Kod` | `Pracownik` (root) | `string` | identyfikator; przy pustym platforma podstawia prefiks + `?` |
| `Imie` | `PracHistoria` (`Last.Imie`) | `string` | wymagane (domyślnie `"?"` z `OnAdded`) |
| `Nazwisko` | `PracHistoria` (`Last.Nazwisko`) | `string` | wymagane (domyślnie `"?"`) |
Pierwszy zapis historii ma okres otwarty (do `Date.MaxValue`); warunki etatu (KADRY-A1 → B) ustawia się
na `Last.Etat` (np. `Etat.Okres`, `Etat.TypUmowy`, `Etat.Wymiar`) — szczegóły w sekcji B.
**Snippet:**
```csharp
var kadry = session.GetKadry();
using (var t = session.Logout(editMode: true))
{
// AddRow zwraca dodany, typowany wiersz; w OnAdded powstaje pierwszy PracHistoria + kalendarz
var pracownik = session.AddRow(new PracownikFirmy());
pracownik.Kod = "555"; // pole rootu
// dane osobowe — na ZAPISIE historii (pierwszy zapis = Last)
pracownik.Last.Nazwisko = "Kowalska";
pracownik.Last.Imie = "Gabriela";
pracownik.Last.PESEL = "94010812345";
t.Commit(); // Commit() w kodzie biznesowym; CommitUI() w workerze/UI
}
session.Save(); // zapis do bazy; tu wykrywane konflikty/duplikaty
```
**Pułapki:**
- **Nie** rób `new Pracownik()` — typ jest abstrakcyjny. Używaj `new PracownikFirmy()`.
- **Nie** dodawaj ręcznie pierwszego `PracHistoria` — robi to `OnAdded`. Ręczne dodanie zapisu
dotyczy dopiero *aktualizacji* danych „od daty" (KADRY-A14).
- Dane osobowe ustawiaj na `Last`/`pracownik[date]`, nie próbuj ich „obejść" przez root — root
deleguje do zapisu, ale zapis jest właściwym miejscem.
- `Kod` bywa polem wymagającym unikalności (zależnie od konfiguracji) — kolizja wybuchnie w
`Save()` jako `RowException` (z `DuplicateKeyException` w `InnerException`).
- Całość w transakcji (`session.Logout(editMode: true)`); brak `Commit()` = rollback przy `Dispose()`.
### KADRY-A2 — Podstawowe dane kadrowe (★)
**Cel:** uzupełnić dane ewidencyjno-identyfikacyjne pracownika (PESEL, NIP, urodzenie, płeć,
obywatelstwo, rodzice, dokument tożsamości, adresy).
**Gdzie leżą pola — root vs PracHistoria:**
| Dana | Lokalizacja | Pole / typ |
|---|---|---|
| Imię, drugie imię, nazwisko | `PracHistoria` | `Imie`, `ImieDrugie`, `Nazwisko: string` |
| Nazwisko rodowe, imię ojca/matki, nazwisko rodowe matki | `PracHistoria` | `NazwiskoRodowe`, `ImieOjca`, `ImieMatki`, `NazwiskoRodoweMatki: string` |
| PESEL | `PracHistoria` (oraz delegat na root) | `PESEL: string` |
| NIP | `PracHistoria` | `NIP: string` |
| Płeć | `PracHistoria` | `Plec: Soneta.Kadry.PłećOsoby` (`Kobieta`/`Mężczyzna`) |
| Data i miejsce urodzenia | `PracHistoria` (subrow `Urodzony`) | `Urodzony.Data: Date`, `Urodzony.Miejsce: string` |
| Obywatelstwo | `PracHistoria` (subrow `Obywatelstwo`) | `Obywatelstwo.Nazwa: string` (słownik), `Obywatelstwo.KodKraju: string` |
| Dokument tożsamości | `PracHistoria` (subrow `Dokument`) | `Dokument.Rodzaj: KodRodzajuDokumentu`, `Dokument.SeriaNumer: string`, `Dokument.WydanyPrzez`, `Dokument.DataWydania/DataWaznosci: Date` |
| Adres zamieszkania / zameldowania / korespondencji | `PracHistoria` | `Adres`, `AdresZameldowania`, `AdresZamieszkania`, `AdresDoKorespondencji: Soneta.Core.Adres` |
| Urząd skarbowy, koszty/ulga | `PracHistoria` (subrow `Podatki`) | `Podatki.UrzadSkarbowy`, `Podatki.KosztyRodzaj`, `Podatki.UlgaMnoznik`, … |
| Kod, numery rachunków US/ZUS | `Pracownik` (root) | `Kod: string`, `NumerRachunkuUS`, `NumerRachunkuZUS` |
**Pułapki:**
- `Plec` jest **wyliczana z PESEL** przez weryfikator — przy poprawnym PESEL nie musisz jej ustawiać;
ustawienie ręczne ma sens dla osób bez PESEL. Typ to enum `PłećOsoby`, nie string.
- `Urodzony`, `Obywatelstwo`, `Dokument`, `Podatki` to **subrowy** (pola złożone) — edytujesz ich
pola (`Last.Urodzony.Data = …`), nie przypisujesz całego obiektu.
- `Adres` (i pozostałe) to property zwracające `Soneta.Core.Adres` — modyfikuj ich pola
(`Last.Adres.Miejscowosc = …`), nie przypisuj `Last.Adres = …`. `KodPocztowy` jest `int`; do
wartości z myślnikiem używaj `Adres.KodPocztowyS` (string).
- PESEL/NIP są danymi wrażliwymi — nie loguj ich (safe-code §12).
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["555"];
using (var t = session.Logout(editMode: true))
{
var ph = pracownik.Last; // bieżący (ostatni) zapis kadrowy
ph.NazwiskoRodowe = "Nowak";
ph.ImieOjca = "Jan";
ph.NIP = "1234563218";
ph.Urodzony.Data = new Date(1994, 1, 8); // subrow Urodzony
ph.Urodzony.Miejsce = "Kraków";
ph.Obywatelstwo.Nazwa = "polskie"; // subrow Obywatelstwo (słownik)
ph.Adres.Ulica = "Wadowicka"; // subrow Adres
ph.Adres.NrDomu = "8A";
ph.Adres.KodPocztowyS = "30-415";
ph.Adres.Miejscowosc = "Kraków";
t.Commit();
}
session.Save();
```
### KADRY-A7 — Dane ubezpieczenia społecznego i zdrowotnego (★)
**Cel:** ustawić/odczytać tytuł ubezpieczenia, oddział NFZ oraz parametry ubezpieczeń społecznych
(emerytalne, rentowe, chorobowe, wypadkowe) i zdrowotnego — daty zgłoszeń i wyrejestrowań.
**Gdzie leżą pola:**
| Dana | Lokalizacja | Pole / typ |
|---|---|---|
| Tytuł ubezpieczenia (kod) | `PracHistoria.Etat.Ubezpieczenia` | `Tyub4: Soneta.Kadry.TytulUbezpieczenia4` (rekord słownika, tytuł 6-znakowy) |
| Zbiorczy stan ubezpieczeń | `PracHistoria.Etat` | `Etat.Ubezpieczenia: Soneta.Kadry.Ubezpieczenia` (subrow) |
| Społeczne (poszczególne) | `…Etat.Ubezpieczenia.*` | `Emerytalne`, `Rentowe`, `Chorobowe`, `Wypadkowe : Soneta.Kadry.Spoleczne` |
| Zdrowotne | `…Etat.Ubezpieczenia.Zdrowotne` | `Soneta.Kadry.Zdrowotne` (subrow) |
| Data objęcia ubezpieczeniami społ. (od) | `…Etat.Ubezpieczenia` (zbiorczo) | `ObowiazkoweOd: Date`**zapisywalne** na zbiorczym subrowie `Ubezpieczenia` |
| Objęcie poszczególnym społ. | `…Ubezpieczenia.Emerytalne` itd. | `Obowiazkowe: bool`, `Dobrowolne: bool`, `DobrowolneOd: Date`, `Do: Date` (wyrej.) — **zapisywalne**; `Od: Date` jest tylko do odczytu (wyliczane) |
| Data objęcia zdrowotnym | `…Ubezpieczenia.Zdrowotne` | `ObowiazkoweOd: Date`**zapisywalne** (asymetria względem `Spoleczne`) |
| Przyczyna wyrejestrowania | `…Ubezpieczenia.Emerytalne.Przyczyna` | `Przyczyna: Soneta.Kadry.Wyrejestrowanie` (subrow z kodem) |
| Oddział NFZ | `PracHistoria.OddzialNFZ` | `OddzialNFZ: Soneta.Kadry.OddzialNFZ` (subrow; `Oddział`, `KodGminy`, `OdDnia`) |
| Tytuł na dzień (odczyt) | `PracHistoria.Etat.Ubezpieczenia` | `WyliczTyubNaDzień(Date)` |
**Pułapki:**
- Cała struktura ubezpieczeń jest **historyczna** (siedzi w `Etat` danego `PracHistoria`) — zmiana
„od daty" wymaga nowego zapisu historii (KADRY-A14), nie nadpisywania bieżącego.
- `Tyub4` to rekord **konfiguracyjnego** słownika `TytulUbezpieczenia4` — pobierz istniejący wpis
przez `session.GetKadry().TytulyUbezpiecz4.WgKodu[kod]`, gdzie **`kod` jest typu `int`** (np. `110`,
`2241`), nie twórz „w locie". (Pole `Tyub`/`TypUbezpieczenia` to starsze typy — używaj `Tyub4`.)
- `OddzialNFZ` to subrow z polem `Oddział` (enum oddziałów) — ustawiasz `OddzialNFZ.Oddział`, nie
całą strukturę.
- `Emerytalne`/`Rentowe`/`Chorobowe`/`Wypadkowe` to subrowy `Spoleczne`. Ustawiasz na nich flagi
`Obowiazkowe`/`Dobrowolne` oraz `DobrowolneOd`/`Do`. **`Od` jest tylko do odczytu** (wyliczane) —
datę objęcia ubezpieczeniami obowiązkowymi ustawiasz **zbiorczo** przez `Ubezpieczenia.ObowiazkoweOd`.
Na subrowie `Zdrowotne` z kolei `ObowiazkoweOd` jest zapisywalne bezpośrednio.
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["555"];
// Odczyt tytułu ubezpieczenia obowiązującego na dziś:
var data = Date.Today;
TytulUbezpieczenia tyubNaDzis = pracownik[data].Etat.Ubezpieczenia.WyliczTyubNaDzień(data);
using (var t = session.Logout(editMode: true))
{
var ub = pracownik.Last.Etat.Ubezpieczenia; // subrow ubezpieczeń bieżącego zapisu
// Tytuł ubezpieczenia (rekord słownika konfiguracyjnego); klucz WgKodu jest po Kod: int:
ub.Tyub4 = session.GetKadry().TytulyUbezpiecz4.WgKodu[110]; // np. 0110 = pracownik
// Data objęcia ubezpieczeniami społecznymi obowiązkowymi — ZBIORCZO (Od na Spoleczne jest read-only):
ub.ObowiazkoweOd = new Date(2026, 1, 1);
ub.Emerytalne.Obowiazkowe = true;
ub.Rentowe.Obowiazkowe = true;
ub.Chorobowe.Obowiazkowe = true;
// Ubezpieczenie zdrowotne — datę objęcia ustawiasz wprost na subrowie Zdrowotne:
ub.Zdrowotne.ObowiazkoweOd = new Date(2026, 1, 1);
// Oddział NFZ (subrow):
pracownik.Last.OddzialNFZ.OdDnia = new Date(2026, 1, 1);
t.Commit();
}
session.Save();
```
### KADRY-A9 — Dane o rodzinie pracownika (★)
**Cel:** ewidencjonować członków rodziny i zgłaszać ich do ubezpieczenia zdrowotnego (ZCNA).
**Kolekcja i typ:** `Pracownik.Rodzina: SubTable<Soneta.Kadry.CzlonekRodziny>` (tabela `Rodzina`,
`GuidedRow` root, child `Pracownik`-a). Nowy członek rodziny tworzony jest konstruktorem
`new CzlonekRodziny(pracownik)`.
**Pola i typy (`CzlonekRodziny`):**
| Pole | Typ | Opis |
|---|---|---|
| `Nazwisko`, `Imie`, `ImieDrugie` | `string` | dane osobowe (wymagane: `Nazwisko`, `Imie`) |
| `PESEL`, `NIP` | `string` | identyfikatory |
| `Urodzony` | `Soneta.Kadry.Urodzony` (subrow) | `Urodzony.Data: Date`, `Urodzony.Miejsce: string` |
| `Dokument` | `Soneta.Kadry.DokumentOsoby` (subrow) | dokument tożsamości |
| `StPokrewienstwa` | `Soneta.Kadry.KodStPokrewienstwa` (enum) | stopień pokrewieństwa |
| `Ubezpieczony` | `bool` | zgłoszony do ubezpieczenia zdrowotnego (ZCNA) |
| `UbezpieczenieOkres` | `Soneta.Types.FromTo` | okres zgłoszenia do ubezpieczenia |
| `StNiepelnosprawnosci` | `Soneta.Kadry.KodStNiepelnosprawnosci` (enum) | stopień niepełnosprawności |
| `WspolneGospDomowe`, `NaUtrzymaniu`, `OdbKsztalcenie` | `bool` | przesłanki zgłoszenia |
| `Adres` | `Soneta.Core.Adres` | adres (gdy inny niż pracownika) |
| `Pracownik` | `Soneta.Kadry.Pracownik` | właściciel (ustawiany ctorem) |
**Pułapki:**
- Zgłoszenie do ubezpieczenia zdrowotnego (ZCNA) realizuje się przez `Ubezpieczony = true` +
`UbezpieczenieOkres` + `StPokrewienstwa` — to z tych pól generowana jest deklaracja ZCNA. Brak
dedykowanego „pola daty wysyłki ZCNA" na członku rodziny.
- `CzlonekRodziny` nie jest historyczny — to płaski child pracownika; okres ubezpieczenia trzyma
pole `UbezpieczenieOkres: FromTo`.
- Konstruktor `new CzlonekRodziny(pracownik)` od razu wiąże rekord z pracownikiem; pojawia się on
w `pracownik.Rodzina`.
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["555"];
using (var t = session.Logout(editMode: true))
{
var dziecko = new CzlonekRodziny(pracownik); // ctor wiąże z pracownikiem
session.AddRow(dziecko);
dziecko.Nazwisko = "Kowalska";
dziecko.Imie = "Zofia";
dziecko.PESEL = "20290512345";
dziecko.Urodzony.Data = new Date(2020, 9, 5);
dziecko.StPokrewienstwa = KodStPokrewienstwa.Dziecko; // wartość enum wg słownika
// Zgłoszenie do ubezpieczenia zdrowotnego (ZCNA):
dziecko.Ubezpieczony = true;
dziecko.UbezpieczenieOkres = new FromTo(new Date(2026, 1, 1), Date.MaxValue);
dziecko.NaUtrzymaniu = true;
t.Commit();
}
session.Save();
// Odczyt aktualnie ubezpieczonych członków rodziny — filtr serwerowy po kolekcji:
foreach (CzlonekRodziny cr in pracownik.Rodzina[(CzlonekRodziny c) => c.Ubezpieczony])
{
// cr.Nazwisko, cr.Imie, cr.StPokrewienstwa
}
```
### KADRY-A10 — Poprzednie miejsca pracy (★)
**Cel:** rejestrować historię zatrudnienia u poprzednich pracodawców i okresy nauki (do wyliczenia
stażu pracy i uprawnień urlopowych).
**Kolekcja i typ:** `Pracownik.HistoriaZatrudnienia: SubTable<Soneta.Kadry.HistoriaZatrudnieniaBase>`
(tabela `HistZatrudnien`, `GuidedRow` root, child `Pracownik`-a). To **inna kolekcja niż
`Pracownik.Historia`** — `Historia` to historia *bieżącego* zatrudnienia (zapisy `PracHistoria`),
a `HistoriaZatrudnienia` to staż u *poprzednich* pracodawców.
`HistoriaZatrudnieniaBase` jest typem bazowym z **konstruktorem `protected`** — nie da się go
utworzyć bezpośrednio. Konkretne typy do tworzenia wpisów to:
- `Soneta.Kadry.HistoriaZatrudnienia` — poprzedni pracodawca (ustawia `Typ = Zatrudnienie`),
- `Soneta.Kadry.UkonczonaSzkola` — okres nauki.
Oba mają publiczny ctor `(Pracownik)`.
**Pola i typy (`HistoriaZatrudnieniaBase`):**
| Pole | Typ | Opis |
|---|---|---|
| `Typ` | `Soneta.Kadry.TypHistoriiZatrudnienia` | rodzaj wpisu (praca / nauka) — ustawiany przez ctor konkretnej klasy, readonly |
| `Nazwa` | `string` | nazwa zakładu pracy / szkoły (wymagane) |
| `Okres` | `Soneta.Types.FromTo` | okres zatrudnienia/nauki |
| `EfektywnyOkres` | `Soneta.Types.FromTo` | okres efektywnie wliczany do stażu |
| `Adres1`, `Adres2` | `string` | adres pracodawcy |
| `Korekta` | `Soneta.Kadry.StazPracy` | ręczna korekta naliczonego stażu |
| `Staz` | `Soneta.Kadry.StazPracyPracownika` | wyliczony staż (kalkulowane) |
| `RodzajDokumentu` | `Soneta.Kadry.RodzajDokumentu` | dokument potwierdzający |
| `Pracownik` | `Soneta.Kadry.Pracownik` | właściciel (relacja, readonly) |
**Pułapki:**
- **Nie** rób `new HistoriaZatrudnieniaBase(...)` — ctor jest `protected`. Twórz konkretny typ:
`new HistoriaZatrudnienia(pracownik)` (praca u poprzedniego pracodawcy, `Typ = Zatrudnienie`) albo
`new UkonczonaSzkola(pracownik)` (nauka).
- `EfektywnyOkres``Okres` — to on (a nie sam `Okres`) decyduje o wliczeniu do stażu; jeśli go nie
ustawisz, obowiązują weryfikatory ciągłości.
- Wpisy są niezależne od `PracHistoria` — nie myl `HistoriaZatrudnienia` (poprzedni pracodawcy)
z `Historia` (zapisy bieżącego zatrudnienia).
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["555"];
using (var t = session.Logout(editMode: true))
{
// konkretny typ: poprzedni pracodawca (Typ = Zatrudnienie ustawia ctor); AddRow zwraca typowany wiersz
var hz = session.AddRow(new HistoriaZatrudnienia(pracownik));
hz.Nazwa = "Poprzednia Firma Sp. z o.o.";
hz.Okres = new FromTo(new Date(2018, 3, 1), new Date(2025, 12, 31));
hz.EfektywnyOkres = hz.Okres;
hz.Adres1 = "ul. Główna 1, Kraków";
t.Commit();
}
session.Save();
// Odczyt historii zatrudnienia (wszystkie typy wpisów, bazowy typ kolekcji):
foreach (HistoriaZatrudnieniaBase hz in pracownik.HistoriaZatrudnienia)
{
// hz.Nazwa, hz.Okres, hz.EfektywnyOkres, hz.Typ
}
```
### KADRY-A14 — Aktualizacja danych historycznych: zmiana „od daty" vs korekta (★)
**Cel:** poprawnie zmienić dane kadrowe — **nowy zapis obowiązujący od wskazanego dnia** (zmiana
warunków: podwyżka, zmiana wymiaru etatu, zmiana danych podatkowych) **kontra korekta istniejącego
zapisu** (poprawa błędu w obecnym okresie). Plus: odczyt zapisu obowiązującego „na dzień".
**Mechanizm `HistorySubTable<PracHistoria>`:**
| Operacja | API | Efekt |
|---|---|---|
| Odczyt zapisu na dzień | `pracownik[date]` (== `(PracHistoria)Historia[date]`) | zwraca zapis, którego `Aktualnosc` zawiera `date` |
| Pierwszy zapis | `pracownik.Historia.GetFirst()` | najstarszy zapis |
| Ostatni zapis | `pracownik.Last` (== `Historia.GetPrev()`) | najświeższy zapis |
| **Nowy zapis „od daty"** | `(PracHistoria)pracownik.Historia.Update(date)` | **klonuje** zapis aktualny na `date`, skraca jego okres do `date-1`, zwraca **nowy** klon z okresem od `date`; nowy klon trzeba dodać do tabeli |
| Okres obowiązywania | `PracHistoria.Aktualnosc: Soneta.Types.FromTo` | „oddo" zapisu (zarządzane przez historię) |
**Wzorzec aktualizacji „od daty" (zmiana warunków od dnia):**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["555"];
var odDnia = new Date(2026, 7, 1);
using (var t = session.Logout(editMode: true))
{
// 1) Update klonuje zapis aktualny na `odDnia` i zwraca nowy klon (okres od `odDnia`).
// Stary zapis zostaje skrócony do dnia poprzedniego.
var nowy = (PracHistoria)pracownik.Historia.Update(odDnia);
// 2) Klon trzeba dodać do tabeli zapisów historii.
pracownik.Module.PracHistorie.AddRow(nowy);
// 3) Na nowym zapisie wprowadzamy zmienione warunki (od `odDnia`):
nowy.Etat.MiejscePracy = "Oddział Kraków"; // np. zmiana miejsca pracy
nowy.Podatki.UlgaMnoznik = 1m; // np. zmiana danych podatkowych
// Uwaga: część pól Etat (np. Etat.Zaszeregowanie.Wymiar) na świeżym klonie potrafi być
// w trybie tylko-do-odczytu (ColReadOnlyException) — odblokowanie zależy od konfiguracji etatu
// (patrz pułapki w sekcji KADRY-B1: bramką jest Etat.Okres). Dla pewności w przykładzie zmieniamy
// pola bezpiecznie zapisywalne (MiejscePracy, dane podatkowe).
t.Commit();
}
session.Save();
```
**Wzorzec korekty istniejącego zapisu (bez nowego okresu):**
```csharp
using (var t = session.Logout(editMode: true))
{
// Modyfikujemy zapis obowiązujący na zadaną datę — bez Update, bez AddRow.
var ph = pracownik[new Date(2026, 3, 15)];
ph.NazwiskoRodowe = "PoprawioneNazwisko"; // korekta w istniejącym okresie
t.Commit();
}
session.Save();
```
**Pułapki:**
- **`Update(date)` + `AddRow(nowy)` to nierozłączna para.** Sam `Update` tworzy „odpięty" klon —
bez `PracHistorie.AddRow(nowy)` zmiana nie zostanie zapisana.
- `Update(date)` rzuca `HistorySubTable.DateDuplicateException`, gdy na `date` już zaczyna się zapis
(`Aktualnosc.From == date`) — nie da się „aktualizować" dwa razy tego samego dnia; wtedy
modyfikuj istniejący zapis (`pracownik[date]`).
- **Korekta** (modyfikacja `pracownik[date]`) zmienia dane w **całym** okresie tego zapisu — używaj
jej tylko do poprawy błędu, nie do „zmiany od dnia".
- `Aktualnosc` (okres zapisu) jest zarządzany przez mechanizm historii — **nie ustawiaj go ręcznie**;
do skrócenia/wstawienia okresu służy `Update`.
- Odczyt „na dzień": `pracownik[date]` zwraca `null`, jeśli dla daty brak zapisu — dla daty sprzed
zatrudnienia. `pracownik.Last` zawsze zwraca najświeższy zapis.
- Aktualizacja danych to operacja na danych operacyjnych pracownika — trzymaj transakcje krótkie
(safe-code §13.1) i obsłuż `RowConflictException` z `Save()` (safe-code §4).
### KADRY-A3 — Adresy (zameldowania / zamieszkania / korespondencyjny)
**Cel:** uzupełnić/odczytać adresy pracownika. Adresy są **historyczne** — leżą na zapisie
`PracHistoria`, dostęp przez `pracownik.Last` (bieżący) lub `pracownik[date]` (na dzień). Każdy adres
to subrow typu `Soneta.Core.Adres` — modyfikujesz jego pola, nie przypisujesz całego obiektu.
**Gdzie leżą pola — `PracHistoria` (subrowy `Soneta.Core.Adres`):**
| Dana | Pole / typ | Uwaga |
|---|---|---|
| Adres podstawowy (kanoniczny) | `Adres: Soneta.Core.Adres` | adres główny pracownika |
| Adres zameldowania | `AdresZameldowania: Soneta.Core.Adres` | |
| Adres zamieszkania | `AdresZamieszkania: Soneta.Core.Adres` | |
| Adres do korespondencji | `AdresDoKorespondencji: Soneta.Core.Adres` | |
| Adres na przelewach | `AdresNaPrzelewach: Soneta.Kadry.AdresPracownikaNaPrzelewach` | osobny typ — adres umieszczany na przelewach |
**Pola subrowa `Soneta.Core.Adres` (zapisywalne, `bazodanowe`):**
| Pole | Typ | Opis |
|---|---|---|
| `Miejscowosc` | `string` | miejscowość |
| `Ulica` | `string` | nazwa ulicy/alei/osiedla |
| `NrDomu` | `string` | numer domu/bloku |
| `NrLokalu` | `string` | numer lokalu |
| `KodPocztowy` | `int` | kod pocztowy (liczbowo) |
| `KodPocztowyS` | `string` | kod pocztowy z myślnikiem (np. `"30-415"`) |
| `Poczta` | `string` | poczta |
| `Gmina`, `Powiat` | `string` | gmina, powiat |
| `Wojewodztwo` | `Soneta.Core.Wojewodztwa` (enum) | województwo |
| `Kraj`, `KodKraju` | `string` | kraj / kod kraju |
| `ZagranicznyKodPocztowy` | `string` | zagraniczny kod pocztowy |
| `Telefon`, `Faks` | `string` | telefon/faks związany z adresem |
| `Pełny` | `string` | sformatowany adres (tylko odczyt) |
**Pułapki:**
- `Adres`, `AdresZameldowania`, … to **subrowy** — modyfikuj pola (`Last.AdresZamieszkania.Ulica = …`),
**nie** przypisuj całego obiektu (`Last.AdresZamieszkania = …` — błąd).
- `KodPocztowy` to `int`; do wartości z myślnikiem używaj `KodPocztowyS` (string).
- Cała struktura jest historyczna — zmiana adresu „od daty" to nowy zapis historii (KADRY-A14), korekta
bieżącego okresu to modyfikacja `pracownik[date]`.
- `Wojewodztwo` to enum `Soneta.Core.Wojewodztwa`, nie string.
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
using (var t = session.Logout(editMode: true))
{
var ph = pracownik.Last;
ph.AdresZamieszkania.Miejscowosc = "Kraków";
ph.AdresZamieszkania.Ulica = "Wadowicka";
ph.AdresZamieszkania.NrDomu = "8A";
ph.AdresZamieszkania.NrLokalu = "12";
ph.AdresZamieszkania.KodPocztowyS = "30-415";
ph.AdresZameldowania.Miejscowosc = "Wieliczka";
ph.AdresDoKorespondencji.Miejscowosc = "Kraków";
t.Commit();
}
session.Save();
// Odczyt adresu na dzień:
Adres adr = pracownik[Date.Today].AdresZamieszkania;
string opis = $"{adr.Ulica} {adr.NrDomu}, {adr.KodPocztowyS} {adr.Miejscowosc}";
```
---
### KADRY-A4 — Dane kontaktowe (e-mail, telefon) i dostęp WWW/Pulpity
**Cel:** ustawić/odczytać dane kontaktowe pracownika (e-mail, telefon komórkowy, WWW). Dane kontaktowe
leżą w subrowie `Kontakt: Soneta.Core.Kontakt` — dostępnym zarówno na rootcie `Pracownik`, jak i na
zapisie historii `PracHistoria` (historyczne). Pracownik dodatkowo udostępnia `EMAIL: string` na rootcie.
**Gdzie leżą pola — subrow `Soneta.Core.Kontakt` (`PracHistoria.Kontakt` / `Pracownik.Kontakt`):**
| Pole | Typ | Opis |
|---|---|---|
| `EMAIL` | `string` | adres poczty elektronicznej |
| `TelefonKomorkowy` | `string` | telefon komórkowy |
| `WWW` | `string` | adres strony internetowej |
| `Skype` | `string` | identyfikator Skype |
| `SkrytkaPocztowa` | `string` | skrytka pocztowa |
**Telefon stacjonarny/faks** — w kontekście adresu: `PracHistoria.Adres.Telefon`, `…Adres.Faks: string`
(patrz KADRY-A3). **Rozbudowane kanały kontaktu** (wiele kontaktów z rodzajem/celem): kolekcja
`PracHistoria.Kontakty: SubTable<Soneta.Core.DaneKontaktowe>` (pola `Kontakt: string`,
`Rodzaj: Soneta.Core.RodzajKontaktu`, `Domyslny: bool`, `Opis: string`).
**Dostęp WWW / Pulpity (IWebOperator):** `Pracownik` implementuje interfejs
`Soneta.…IWebOperator`. Konto dostępu do Pulpitów (operator web, login, uprawnienia) **nie jest
zwykłym zapisywalnym polem** pracownika — jest zarządzane osobnym mechanizmem operatorów/uprawnień
modułu web (poza prostym kontraktem ustawiania pól na pracowniku). W publicznym kontrakcie danych
kartotekowych operujesz danymi kontaktowymi (e-mail/telefon/WWW), a powiązanie operatora web jest
realizowane przez konfigurację operatorów, nie przez `pracownik.Last`.
**Pułapki:**
- `Kontakt` to **subrow** — modyfikuj pola (`Last.Kontakt.EMAIL = …`), nie przypisuj całego obiektu.
- Pole nazywa się `EMAIL` (wielkimi literami) — uwaga na wielkość liter.
- E-mail/telefon w kontekście „na przelewach"/PPK to inne pola (`OdpisPPK.Email`,
`OdpisPPK.TelefonKomorkowy`) — nie myl z kontaktem osobowym.
- Dostęp do Pulpitu (IWebOperator) nie jest częścią `PracHistoria` — nie szukaj „pola WWW dostępu"
na zapisie kadrowym.
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
using (var t = session.Logout(editMode: true))
{
var k = pracownik.Last.Kontakt; // subrow Kontakt bieżącego zapisu
k.EMAIL = "g.kowalska@firma.pl";
k.TelefonKomorkowy = "600100200";
k.WWW = "https://firma.pl/g.kowalska";
t.Commit();
}
session.Save();
// Odczyt:
string mail = pracownik.Last.Kontakt.EMAIL;
```
---
### KADRY-A5 — Rachunki bankowe (rachunek do przelewu wynagrodzenia)
**Cel:** zarejestrować/odczytać rachunki bankowe pracownika oraz wskazać rachunek główny do przelewu
wynagrodzenia.
**Kolekcja i typ:** `Pracownik.Rachunki: SubTable<Soneta.Kasa.RachunekBankowyPodmiotu>` (rachunki są
na rootcie pracownika, nie w historii). Rachunek główny (domyślny) zwraca
`Pracownik.DomyslnyRachunek: Soneta.Kasa.RachunekBankowyPodmiotu`. Numery rachunków US/ZUS pracownika
to osobne pola rootu: `Pracownik.NumerRachunkuUS: Soneta.Core.NumerRachunkuUS`,
`Pracownik.NumerRachunkuZUS: Soneta.Core.NumerRachunkuZUS`.
**Pola i typy (`Soneta.Kasa.RachunekBankowyPodmiotu`):**
| Pole | Typ | Opis |
|---|---|---|
| `Rachunek` | `Soneta.Kasa.RachunekBankowy` (subrow) | właściwy rachunek; `Rachunek.Numer: Soneta.Kasa.NumerRachunku`, `Rachunek.Bank: Soneta.Kasa.IBank` |
| `Rachunek.Numer.Pełny` / `.PełnyNRB` | `string` | pełny numer rachunku (do odczytu/ustawienia) |
| `Domyslne` | `bool` | rachunek domyślny (do odczytu — odpowiada `DomyslnyRachunek`) |
| `Priorytet` | `int` | priorytet rachunku |
| `Procent` | `Soneta.Types.Percent` | udział % (przy podziale wynagrodzenia na rachunki) |
| `Kwota` | `Soneta.Types.Currency` | kwota stała (przy podziale wynagrodzenia) |
| `Nazwa1`, `Nazwa2` | `string` | linie informacji na przelewie |
| `Oddzial` | `Soneta.Core.OddzialFirmy` | oddział |
| `Blokada` | `bool` | blokada rachunku |
| `Podmiot` | `Soneta.Kasa.IPodmiotKasowy` | właściciel (pracownik) |
**Pułapki:**
- `RachunekBankowyPodmiotu` to typ z modułu `Soneta.Kasa` — element kolekcji `pracownik.Rachunki`.
- Rachunek główny do wynagrodzenia odczytujesz przez `pracownik.DomyslnyRachunek` (nie iteruj
kolekcji szukając `Domyslne == true`, gdy wystarczy property).
- `Rachunek` to **subrow** — numer ustawiasz na `r.Rachunek.Numer` (typ biznesowy `NumerRachunku`),
nie jako prosty string na poziomie `RachunekBankowyPodmiotu`.
- Numer rachunku to typ biznesowy (`NumerRachunku`/`NumerRachunkuPodmiotu`), z walidacją IBAN/NRB —
nie traktuj go jak zwykły `string`.
**Snippet (odczyt — bezpieczny, bez zależności od konstruktora rachunku):**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
// Rachunek główny do przelewu wynagrodzenia:
RachunekBankowyPodmiotu glowny = pracownik.DomyslnyRachunek;
if (glowny != null)
{
string numer = glowny.Rachunek.Numer.Pełny;
}
// Wszystkie rachunki pracownika:
foreach (RachunekBankowyPodmiotu r in pracownik.Rachunki)
{
bool czyDomyslny = r.Domyslne;
int priorytet = r.Priorytet;
}
```
---
### KADRY-A6 — Dane podatkowe (PIT)
**Cel:** ustawić/odczytać dane podatkowe pracownika: koszty uzyskania przychodu, ulgę podatkową
(PIT-2), próg/typ progów podatkowych, urząd skarbowy oraz numer rachunku US. Dane są **historyczne**
subrow `PracHistoria.Podatki`; numer rachunku US to pole rootu pracownika.
**Gdzie leżą pola — subrow `PracHistoria.Podatki` (`pracownik.Last.Podatki`):**
| Dana | Pole / typ | Uwaga |
|---|---|---|
| Rodzaj kosztów uzyskania | `Podatki.KosztyRodzaj: Soneta.Kadry.RodzajKosztowUzyskania` (enum) | podstawowe / podwyższone / brak |
| Mnożnik kosztów | `Podatki.KosztyMnoznik: decimal` | np. 1 / 0 |
| Koszty autorskie 50% | `Podatki.Koszty50Procent: Percent`, `Koszty50Limit: decimal`, `Koszty50NieNaliczajOd: YearMonth`, `Koszty50NieNaliczajOdDnia: Date` | koszty autorskie |
| Ulga podatkowa (mnożnik) | `Podatki.UlgaMnoznik: decimal` | PIT-2: pełna kwota zmniejszająca = mnożnik (np. `1m`), `0` = brak |
| Część ulgi (podział PIT-2) | `Podatki.UlgaCzesc: Soneta.Kadry.UlgaPodatkowaCzesc` (enum) | 1/1, 1/2, 1/3 (podział między płatników) |
| Limit ulgi | `Podatki.UlgaLimit: bool` | |
| Ulga „klasa średnia" / emeryt / duża rodzina / zagranica | `Podatki.UlgaKlasaSrednia`, `UlgaEmeryt`, `UlgaDuzaRodzina`, `UlgaZagranica: bool`; `UlgaZagranicaOd/Do: int` | dodatkowe ulgi |
| Typ progów podatkowych | `Podatki.TypProgow: Soneta.Kadry.TypProgowPodatkowych` (enum) | standardowe / indywidualne |
| Progi podatkowe (indywidualne) | `Podatki.ProgiPodatkowe: SubTable` | gdy `TypProgow` = indywidualne |
| Podwyższona zaliczka (próg) | `Podatki.PodwProg2019: bool` | |
| Naliczanie PIT po 26 r.ż. (ulga dla młodych) | `Podatki.Pit26: Soneta.Kadry.NaliczajPit26` (enum) | „zerowy PIT" dla młodych |
| Rezygnacja z rozp. 07.01.22 | `Podatki.RezygnacjaRozp070122`, `…Umowa: bool` | |
| Kwota wolna przy umowie | `Podatki.UmowaKwotaWolna: bool` | |
| Adres na PIT = zameldowania | `Podatki.NaPITAdresZameldowania: bool` | |
| Urząd skarbowy | `Podatki.UrzadSkarbowy: Soneta.Core.IPodmiotUI` (ref); `UrzadSkarbowyEx: Soneta.CRM.UrzadSkarbowy` | pobierz istniejący US |
| Numer rachunku US (pracownika) | `Pracownik.NumerRachunkuUS: Soneta.Core.NumerRachunkuUS` (root) | `NumerRachunkuUS.Numer/.Pełny` |
**Pułapki:**
- `Podatki` to **subrow** zapisu historii — modyfikuj pola (`Last.Podatki.UlgaMnoznik = …`), nie
przypisuj całego obiektu; zmiana „od daty" to nowy zapis historii (KADRY-A14).
- PIT-2 (ulga) reprezentowana jest przez `UlgaMnoznik` (pełna/część kwoty zmniejszającej) oraz
`UlgaCzesc` (podział między płatników) — nie ma jednego pola „PIT2 bool".
- `KosztyRodzaj`, `TypProgow`, `UlgaCzesc`, `Pit26` to **enumy**, nie string.
- `UrzadSkarbowy` to **referencja** do istniejącego podmiotu — nie twórz „w locie".
- `NumerRachunkuUS` jest na **rootcie** pracownika, nie w `Podatki`.
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
using (var t = session.Logout(editMode: true))
{
var p = pracownik.Last.Podatki;
p.KosztyRodzaj = RodzajKosztowUzyskania.JedenStosPracy; // JedenStosPracy/JedenStos25/WiecejStosPracy/WiecejStos25
p.UlgaMnoznik = 1m; // pełna kwota zmniejszająca (PIT-2)
p.UlgaCzesc = UlgaPodatkowaCzesc.Ulga112; // podział PIT-2: Ulga112/Ulga124/Ulga136
p.TypProgow = TypProgowPodatkowych.Standardowe; // enum
t.Commit();
}
session.Save();
// Odczyt:
decimal mnoznikUlgi = pracownik.Last.Podatki.UlgaMnoznik;
RodzajKosztowUzyskania koszty = pracownik.Last.Podatki.KosztyRodzaj;
```
---
### KADRY-A8 — Pozostałe dane ubezpieczeniowe / informacje ZUS (oddział, kod)
**Cel:** ustawić/odczytać dodatkowe dane ZUS pracownika — oddział ZUS oraz dodatkowe świadczenia ZUS
(emerytury/renty z dodatkowymi świadczeniami). Dane są **historyczne** — subrow
`PracHistoria.DodSwiadczeniaZUS`. (Tytuł ubezpieczenia i parametry ubezpieczeń społ./zdrow. opisuje KADRY-A7.)
**Gdzie leżą pola — subrow `PracHistoria.DodSwiadczeniaZUS: Soneta.Kadry.DodatkoweŚwiadczeniaZUS`:**
| Pole | Typ | Opis |
|---|---|---|
| `OddzialZUS` | `Soneta.CRM.OddziałZUS` (ref) | oddział ZUS (referencja do podmiotu/słownika ZUS) |
| `Rodzaj` | `Soneta.Kadry.RodzajeDodatkowychŚwiadczeńZUS` (enum) | rodzaj dodatkowego świadczenia ZUS |
| `Okres` | `Soneta.Types.FromTo` | okres obowiązywania świadczenia |
| `Numer` | `string` | numer (decyzji/świadczenia) |
**Oddział NFZ (komplementarny do ZUS):** `PracHistoria.OddzialNFZ: Soneta.Kadry.OddzialNFZ`
pola `OddzialNFZ.Oddział: OddziałNFZ` (enum oddziałów), `OddzialNFZ.KodGminy: string`,
`OddzialNFZ.OdDnia: Date` (patrz też KADRY-A7).
**Pułapki:**
- `DodSwiadczeniaZUS` to **subrow** — modyfikuj pola (`Last.DodSwiadczeniaZUS.OddzialZUS = …`).
- `OddzialZUS` to **referencja** (`Soneta.CRM.OddziałZUS`) do istniejącego rekordu — pobierz
istniejący, nie twórz „w locie".
- `Rodzaj` to **enum** `RodzajeDodatkowychŚwiadczeńZUS`, nie string.
- **Cały subrow `DodSwiadczeniaZUS` bywa tylko-do-odczytu** na świeżym zapisie (rzuca
`ColReadOnlyException` nawet dla `Numer`) — pola te aktywuje dopiero zainicjowanie świadczenia
w kreatorze/UI. Zapisywalne wprost jest `OddzialNFZ.OdDnia`.
- Zmiana „od daty" to nowy zapis historii (KADRY-A14).
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
// Odczyt dodatkowych świadczeń ZUS i oddziału NFZ na dziś:
var ph = pracownik.Last;
RodzajeDodatkowychŚwiadczeńZUS rodzaj = ph.DodSwiadczeniaZUS.Rodzaj;
FromTo okres = ph.DodSwiadczeniaZUS.Okres;
var oddzialNfz = ph.OddzialNFZ.Oddział; // enum oddziałów NFZ
using (var t = session.Logout(editMode: true))
{
// Zapisywalne wprost na świeżym zapisie: data objęcia oddziałem NFZ.
ph.OddzialNFZ.OdDnia = new Date(2026, 1, 1);
t.Commit();
}
session.Save();
```
---
### KADRY-A11 — Wykształcenie, znajomość języków obcych, służba wojskowa
**Cel:** ewidencjonować wykształcenie, znane języki obce oraz dane służby wojskowej pracownika.
Wykształcenie i wojsko to subrowy zapisu historii (`PracHistoria`); języki obce to kolekcja na rootcie
pracownika.
**Wykształcenie — subrow `PracHistoria.Wyksztalcenie: Soneta.Kadry.Wyksztalcenie`:**
| Pole | Typ | Opis |
|---|---|---|
| `Kod` | `Soneta.Kadry.KodWyksztalcenia` (enum) | poziom/rodzaj wykształcenia |
| `StopienNaukowy` | `string` | stopień naukowy |
| `TytulNaukowy` | `string` | tytuł naukowy |
(Kod wykształcenia GUS jest osobno — patrz KADRY-A12: `PracHistoria.GUS.KodWyksztalcenia`.)
**Języki obce — kolekcja `Pracownik.JęzykiObce: SubTable<Soneta.Kadry.ZnajomośćJęzykaObcego>`:**
| Pole | Typ | Opis |
|---|---|---|
| `Jezyk` | `Soneta.Kadry.DefinicjaJęzykaObcego` (ref słownik) | język |
| `Mowa` | `Soneta.Kadry.DefinicjaStopiaZnajomościJęzykaObcego` (ref) | stopień znajomości w mowie |
| `Pismo` | `Soneta.Kadry.DefinicjaStopiaZnajomościJęzykaObcego` (ref) | stopień znajomości w piśmie |
| `Zaswiadczenie` | `string` | nr/opis zaświadczenia |
| `DataWydaniaZaswiadczenia` | `Soneta.Types.Date` | data wydania zaświadczenia |
| `Uwagi` | `Soneta.Business.MemoText` | uwagi |
**Służba wojskowa — subrow `PracHistoria.Wojsko: Soneta.Kadry.Wojsko`:**
| Pole | Typ | Opis |
|---|---|---|
| `Stosunek` | `Soneta.Kadry.KodStosDoSluzbyWojskowej` (enum) | stosunek do służby wojskowej |
| `KategoriaZdrowia` | `Soneta.Kadry.KategoriaZdrowia` (enum) | kategoria zdrowia (A, B, …) |
| `Stopien` | `string` | stopień wojskowy |
| `NrKsiazeczki` | `string` | nr książeczki wojskowej |
| `NrSpecjalnosci` | `string` | nr specjalności wojskowej |
| `WKU` | `string` | właściwa WKU |
| `PrzydzialMobilizacyjny` | `string` | przydział mobilizacyjny |
| `Podlega` | `bool` | czy podlega obowiązkowi (odczyt) |
**Pułapki:**
- `Wyksztalcenie` i `Wojsko` to **subrowy** `PracHistoria` (historyczne) — modyfikuj pola, zmiana
„od daty" przez KADRY-A14. `JęzykiObce` to **kolekcja na rootcie** pracownika (nie historyczna).
- `Jezyk`, `Mowa`, `Pismo` to **referencje** do rekordów słownika (`DefinicjaJęzykaObcego`,
`DefinicjaStopiaZnajomościJęzykaObcego`) — pobierz istniejące, nie twórz „w locie".
- `Kod` (wykształcenie), `Stosunek`, `KategoriaZdrowia` to **enumy**, nie string.
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
using (var t = session.Logout(editMode: true))
{
// Wykształcenie (subrow historii):
pracownik.Last.Wyksztalcenie.Kod = KodWyksztalcenia.Wyzsze; // enum
pracownik.Last.Wyksztalcenie.TytulNaukowy = "mgr inż.";
// Służba wojskowa (subrow historii):
pracownik.Last.Wojsko.Stosunek = KodStosDoSluzbyWojskowej.Rezerwa; // NieDotyczy/NiePodlega/Przedpoborowy/Poborowy/Rezerwa/Inne
pracownik.Last.Wojsko.KategoriaZdrowia = KategoriaZdrowia.A;
pracownik.Last.Wojsko.NrKsiazeczki = "AB123456";
t.Commit();
}
session.Save();
// Odczyt znajomości języków obcych (kolekcja na rootcie):
foreach (ZnajomośćJęzykaObcego j in pracownik.JęzykiObce)
{
var jezyk = j.Jezyk; // DefinicjaJęzykaObcego
var mowa = j.Mowa; // DefinicjaStopiaZnajomościJęzykaObcego
}
```
---
### KADRY-A12 — Dane statystyczne GUS, kod zawodu GUS
**Cel:** ustawić/odczytać dane statystyczne GUS pracownika (kod wykształcenia GUS, rodzaj zatrudnienia
GUS, przesłanki statystyczne) oraz kod wykonywanego zawodu. Dane są **historyczne** — subrow
`PracHistoria.GUS`; kod zawodu siedzi w `Etat`.
**Dane statystyczne — subrow `PracHistoria.GUS: Soneta.Kadry.StatystykaGUS`:**
| Pole | Typ | Opis |
|---|---|---|
| `KodWyksztalcenia` | `Soneta.Kadry.KodWykształceniaGUS` (enum) | kod wykształcenia wg GUS |
| `RodzajZatrudnienia` | `Soneta.Kadry.RodzajZatrudnieniaGUS` (enum) | rodzaj zatrudnienia wg GUS |
| `PopMiejsceZatrudnienia` | `Soneta.Kadry.PopMiejsceZatrudnienia` (enum) | poprzednie miejsce zatrudnienia |
| `GlowneMiejscePracy` | `bool` | główne miejsce pracy |
| `PierwszaPraca` | `bool` | pierwsza praca |
| `PracaWNocy` | `bool` | praca w nocy |
| `StRobotnicze` | `bool` | stanowisko robotnicze |
| `SezonowyDorywczy` | `bool` | zatrudnienie sezonowe/dorywcze |
| `PraceInterwencyjne` | `bool` | prace interwencyjne |
**Kod wykonywanego zawodu — `PracHistoria.Etat`:**
| Pole | Typ | Opis |
|---|---|---|
| `Etat.KodWykonywanegoZawodu` | `int` | kod zawodu GUS (liczbowo) |
| `Etat.KodWykonywanegoZawoduLnk` | `Soneta.Kadry.KodWykonywanegoZawodu` (ref/odczyt) | dowiązany rekord słownika kodu zawodu |
**Pułapki:**
- `GUS` to **subrow** `PracHistoria` (historyczny) — modyfikuj pola; zmiana „od daty" przez KADRY-A14.
- `KodWyksztalcenia` (GUS, enum `KodWykształceniaGUS`) to **inne pole** niż KADRY-A11
`Wyksztalcenie.Kod` (enum `KodWyksztalcenia`) — nie myl ich.
- `Etat.KodWykonywanegoZawodu` to `int`; `…Lnk` to dowiązanie do słownika (kanonicznie ustawiasz
kod liczbowo, dowiązanie jest pochodne).
- Pola GUS to enumy/bool, nie string.
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
using (var t = session.Logout(editMode: true))
{
var ph = pracownik.Last;
ph.GUS.KodWyksztalcenia = KodWykształceniaGUS.Wyższe; // enum GUS (uwaga na diakrytyk)
ph.GUS.GlowneMiejscePracy = true;
ph.GUS.PierwszaPraca = false;
ph.Etat.KodWykonywanegoZawodu = 251401; // kod zawodu GUS (int)
t.Commit();
}
session.Save();
// Odczyt:
KodWykształceniaGUS kw = pracownik.Last.GUS.KodWyksztalcenia;
int kodZawodu = pracownik.Last.Etat.KodWykonywanegoZawodu;
```
---
### KADRY-A13 — PFRON i niepełnosprawność / schorzenia
**Cel:** ewidencjonować dane o niepełnosprawności (stopień, orzeczenie, okresy) oraz dane PFRON
(dofinansowania, schorzenia szczególne). Dane są **historyczne** — subrow `PracHistoria.PFRON`.
**Gdzie leżą pola — subrow `PracHistoria.PFRON: Soneta.Kadry.DanePFRON`:**
| Dana | Pole / typ | Uwaga |
|---|---|---|
| Stopień niepełnosprawności | `PFRON.Stopien: Soneta.Kadry.StNiepełnosprawności` (enum) | stopień orzeczony |
| Stopień wg PFRON | `PFRON.StopienPFRON: Soneta.Kadry.KodStNiepelnosprawnosciPFRON` (enum) | klasyfikacja PFRON |
| Okres orzeczenia/uprawnień | `PFRON.Okres: Soneta.Types.FromTo` | okres niepełnosprawności |
| Data orzeczenia | `PFRON.DataOrzeczenia: Soneta.Types.Date` | |
| Data dostarczenia orzeczenia | `PFRON.DataDostarczenia: Soneta.Types.Date` | |
| Data wniosku / zaświadczenia | `PFRON.DataWniosku`, `PFRON.DataZaswiadczenia: Date` | |
| Data zgłoszenia do ewidencji PFRON | `PFRON.DataZgloszeniaDoEwidencji: Date` | |
| Organ wydający orzeczenie | `PFRON.OrganWydajacyOrzeczenie: Soneta.Kadry.OrganWydajacyOrzeczenie` (enum) | |
| Schorzenie szczególne (flaga) | `PFRON.SzczegolneSchorzenie: bool`, `PFRON.SzczegolneSchorzeniePFRON: bool` | |
| Typ schorzenia | `PFRON.TypSchorzenia: Soneta.Kadry.SzczegolneSchorzenia` (enum) | rodzaj schorzenia |
| Schorzenia SOD (14) | `PFRON.TypSchorzeniaSOD`, `…2SOD`, `…3SOD`, `…4SOD: Soneta.Kadry.SzczególneSchorzeniaSOD` (enum) | schorzenia dla dofinansowania SOD |
| Lista schorzeń SOD (odczyt) | `PFRON.SchorzeniaSOD: IEnumerable<SzczególneSchorzeniaSOD>` | wyliczane |
| Efekt zachęty | `PFRON.EfektZachety: bool` | warunek dofinansowania |
| Pomoc publiczna | `PFRON.PomocPubliczna: Soneta.Kadry.StanowiPomocPubliczną` (enum) | |
| Dofinansowanie dodatkowe SOD | `PFRON.DodatkoweDofinansowanieSOD: bool` | |
| Urlop dodatkowy (niepełnospr.) | `PFRON.NaliczajUrlopDodatkowy: bool`, `…Od: Date` | |
| Wymiar urlopu podstawowego | `PFRON.WymiarUPodstawowego: Soneta.Types.Fraction` | |
| Wiek emerytalny od | `PFRON.WiekEmerytalnyOd: Date` | |
| Zgoda na przekazanie danych | `PFRON.ZgodaNaPrzekazanieDanych: bool` | |
(Zgody na pracę powyżej norm dla osób niepełnosprawnych są na `Etat`:
`Etat.PracownikNiepelnosprawnyZgodaNaPrace8h`, `…ZgodaNaPraceNadgodziny`,
`…ZgodaNaPraceWPorzeNocnej: bool`.)
**Pułapki:**
- `PFRON` to **subrow** `PracHistoria` (historyczny) — modyfikuj pola; zmiana „od daty" przez KADRY-A14.
- `Stopien`, `StopienPFRON`, `TypSchorzenia`, `…SOD`, `OrganWydajacyOrzeczenie`, `PomocPubliczna`
to **enumy**, nie string.
- `SchorzeniaSOD` (lista) jest **wyliczana** — schorzenia ustawiasz przez pola `TypSchorzeniaSOD…4SOD`.
- Daty to `Soneta.Types.Date`, okres to `FromTo` — nie `DateTime`.
**Snippet:**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
using (var t = session.Logout(editMode: true))
{
var pfron = pracownik.Last.PFRON;
pfron.Stopien = StNiepełnosprawności.Umiarkowany; // enum
pfron.Okres = new FromTo(new Date(2026, 1, 1), new Date(2028, 12, 31));
pfron.DataOrzeczenia = new Date(2025, 12, 1);
pfron.DataDostarczenia = new Date(2025, 12, 15);
pfron.SzczegolneSchorzenie = true;
pfron.TypSchorzeniaSOD = SzczególneSchorzeniaSOD.ChoróbPsychiczna; // wg słownika SOD
t.Commit();
}
session.Save();
// Odczyt stopnia i okresu niepełnosprawności na dzień:
var ph = pracownik[Date.Today];
StNiepełnosprawności stopien = ph.PFRON.Stopien;
FromTo okresNiepeln = ph.PFRON.Okres;
```
### KADRY-A15 — Odczyt danych pracownika „na dzień" (★)
**Cel:** pobrać właściwy rekord historyczny `PracHistoria` obowiązujący dla zadanej daty — i odróżnić
**odczyt** (nie zmienia historii) od **zmiany „od daty"** z KADRY-A14 (`Update` + `AddRow`). Receptura
czysto odczytowa: idealna do uruchomienia na bazie Demo (kody `"006"``"009"`).
**API odczytu — `Pracownik` + `HistorySubTable<PracHistoria>`:**
| Operacja | API | Zwraca |
|---|---|---|
| Zapis obowiązujący na dzień | `pracownik[date]` (indeksator, `Item[Date]`) | `PracHistoria` którego `Aktualnosc` zawiera `date`, albo `null` dla daty sprzed zatrudnienia |
| Równoważnie przez kolekcję | `pracownik.Historia[date]` | jw. (indeksator `HistorySubTable<T>.Item[Date]`) |
| Najstarszy (pierwszy) zapis | `pracownik.Historia.GetFirst()` | `PracHistoria` (pierwszy okres zatrudnienia) |
| Najświeższy (ostatni) zapis | `pracownik.Last` (== `Historia.GetLast()`) | `PracHistoria` (zawsze niepusty dla istniejącego pracownika) |
| Sąsiedni zapis | `Historia.GetPrev(ph)` / `Historia.GetNext(ph)` | poprzedni / następny zapis względem podanego |
| Okres obowiązywania zapisu | `PracHistoria.Aktualnosc: FromTo` | „oddo" zapisu (read-only z punktu widzenia kodu — zarządza historia) |
**Różnica odczyt (KADRY-A15) vs zmiana (KADRY-A14):**
| Aspekt | KADRY-A15 — odczyt | KADRY-A14 — zmiana „od daty" |
|---|---|---|
| Wywołanie | `pracownik[date]` | `pracownik.Historia.Update(date)` + `PracHistorie.AddRow(nowy)` |
| Efekt na historii | **żaden** (nie tworzy/skraca zapisów) | klonuje zapis aktualny na `date`, skraca poprzedni do `date-1`, dodaje nowy |
| Transakcja | niepotrzebna (sam odczyt) | wymagana (`session.Logout(editMode: true)` + `Save()`) |
| Zwraca | istniejący zapis (lub `null`) | **nowy** klon do uzupełnienia |
**Snippet (odczyt — bez transakcji):**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
// 1) Zapis obowiązujący na konkretny dzień:
var data = new Date(2026, 3, 15);
PracHistoria phNaDzien = pracownik[data]; // == pracownik.Historia[data]
if (phNaDzien != null)
{
string nazwisko = phNaDzien.Nazwisko;
FromTo okresZapisu = phNaDzien.Aktualnosc; // okres obowiązywania tego zapisu
Fraction wymiar = phNaDzien.Etat.Wymiar; // warunki etatu „na dzień"
}
// 2) Pierwszy i ostatni zapis historii:
PracHistoria pierwszy = pracownik.Historia.GetFirst(); // najstarszy
PracHistoria ostatni = pracownik.Last; // najświeższy (== GetLast())
// 3) Odczyt aktualny „na dziś" (uwzględnia datę biznesową aplikacji):
PracHistoria phDzis = pracownik[Date.Today];
```
**Pułapki:**
- `pracownik[date]` zwraca **`null`** dla daty sprzed pierwszego zapisu (przed zatrudnieniem) — zawsze
sprawdzaj `null` przy datach historycznych. `pracownik.Last` jest niepusty dla istniejącego pracownika.
- Indeksator to **tylko odczyt** — nie próbuj „ustawiać" `pracownik[date] = …`. Zmiana danych w okresie
to korekta (`pracownik[date].Pole = …` w transakcji, KADRY-A14) lub nowy zapis (`Update`, KADRY-A14).
- `Aktualnosc` jest zarządzana przez mechanizm historii — odczytujesz, nie ustawiasz.
- `data` to `Soneta.Types.Date`, nie `DateTime`; do „dziś" używaj `Date.Today` (safe-code §10.2).
- Czysty odczyt nie wymaga transakcji edycyjnej — nie otwieraj `Logout(editMode: true)` bez potrzeby.
### KADRY-A16 — Powiązanie pracownika z kontrahentem (★)
**Cel:** powiązać pracownika z istniejącym kontrahentem (np. gdy pracownik jest jednocześnie
kontrahentem firmy). Dwie drogi: bezpośrednie ustawienie relacji na rootcie **albo** worker
„Powiąż z kontrahentem".
**Publiczny kontrakt — pole relacji na `Pracownik` (root):**
| Pole | Typ | Rodzaj | Uwaga |
|---|---|---|---|
| `PowiazanyKontrahent` | `Soneta.CRM.Kontrahent` | bazodanowe, **zapisywalne** | referencja do istniejącego kontrahenta; `null` = brak powiązania |
**Worker (alternatywa, ta sama operacja):** `Soneta.Kadry.PowiazZKontrahentemWorker`
(`[Action("Powiąż z kontrahentem")]`, metoda `Powiaz()`):
| Składnik | Sygnatura | Uwaga |
|---|---|---|
| Pracownik | `Pracownik { get; set; }` | `[Context]` — pracownik do powiązania |
| Parametry | `Prms: Soneta.Kadry.MyParams` | `MyParams.Kontrahent: Soneta.CRM.Kontrahent` — kontrahent docelowy |
| Akcja | `void Powiaz()` | ustawia powiązanie (działa na danych sesji workera) |
**Snippet (bezpośrednio — zalecane w kodzie biznesowym):**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
var kontrahent = session.GetCRM().Kontrahenci.WgKodu["KLIENT01"]; // istniejący kontrahent
using (var t = session.Logout(editMode: true))
{
pracownik.PowiazanyKontrahent = kontrahent; // relacja na rootcie pracownika
t.Commit();
}
session.Save();
// Odczyt powiązania:
Kontrahent powiazany = pracownik.PowiazanyKontrahent; // null gdy brak
```
**Snippet (przez worker — gdy chcesz przejść tą samą ścieżką co UI):**
```csharp
using (var t = session.Logout(editMode: true))
{
var worker = new PowiazZKontrahentemWorker
{
Pracownik = pracownik,
Prms = new MyParams(context) { Kontrahent = kontrahent },
};
worker.Powiaz();
t.CommitUI(); // worker uruchamiany „jak z UI"
}
session.Save();
```
**Pułapki:**
- `Kontrahent` i `Pracownik` muszą pochodzić z **tej samej sesji** (safe-code §2.1) — kontrahenta z innej
sesji przepuść przez `session.Get(...)`.
- Relacja wskazuje **istniejący** rekord kontrahenta — nie twórz kontrahenta „w locie" w tym scenariuszu.
- `MyParams` ma konstruktor `MyParams(Context context)` — wymaga `Context` (dlatego ścieżka workera ma sens
głównie z UI). W czystym kodzie biznesowym prościej ustawić `PowiazanyKontrahent` wprost.
- W teście jednostkowym preferuj bezpośrednie pole `PowiazanyKontrahent` — nie wymaga `Context`.
### KADRY-A17 — Przeniesienie do archiwum i przywrócenie (★)
**Cel:** przenieść pracownika do archiwum (po zakończeniu zatrudnienia) oraz przywrócić go z archiwum.
**Operacja przenoszenia/przywracania jest dostępna wyłącznie przez workery** — manager `Archiwum`
udostępnia tylko odczyt statusu.
**Publiczny kontrakt odczytu — `Pracownik`:**
| Składnik | Typ | Rodzaj | Uwaga |
|---|---|---|---|
| `Archiwum` | `Pracownik.ArchiwumManager` | manager (read-only API) | `Archiwum.Status: InformacjeOArchiwum`, `Archiwum.Anonimizowany: bool`, `Archiwum.Okresy: Periods` |
| `ArchiwumInfo` | `Soneta.Kadry.InformacjeOArchiwum` | bazodanowe | bieżąca informacja o archiwizacji |
| `InformacjeOArchiwum` | `FromToSubTable<Soneta.Kadry.PracownikWArchiwum>` | kolekcja | historia okresów w archiwum (`PracownikWArchiwum.Okres: FromTo`) |
> **`ArchiwumManager` nie ma publicznej metody Przenieś/Przywróć** — wystawia jedynie właściwości
> tylko-do-odczytu (`Status`, `Anonimizowany`, `Okresy`). Zmiana stanu archiwum następuje wyłącznie
> przez workery poniżej.
**Workery (jedyna droga zmiany stanu):**
| Worker | Akcja (menu) | Metoda | Parametry |
|---|---|---|---|
| `Pracownik.PrzenieśDoArchiwumWorker` | „Archiwum/Przenieś do archiwum" | `void PrzenieśDoArchiwum()` | `Pracownik { get; set; }` (`[Context]`, pojedynczy) |
| `Pracownik.PrzywróćZArchiwumWorker` | „Archiwum/Przywróć z archiwum" | `void PrzywróćZArchiwum()` | `Pracownik { get; set; }` (`[Context]`, pojedynczy) |
| `Pracownik.PrzenieśDoArchiwumLstWorker` | „Operacje seryjne/Archiwum/Przenieś do archiwum…" | `void PrzenieśDoArchiwum()` | `Pracownicy: Pracownik[]` (grupowo) |
| `Pracownik.PrzywróćZArchiwumLstWorker` | „Operacje seryjne/Archiwum/Przywróć z archiwum…" | `void PrzywróćZArchiwum()` | `Pracownicy: Pracownik[]` (grupowo) |
**Snippet (programowe wywołanie workera — pojedynczy pracownik):**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
// Przeniesienie do archiwum:
using (var t = session.Logout(editMode: true))
{
var worker = new Pracownik.PrzenieśDoArchiwumWorker { Pracownik = pracownik };
worker.PrzenieśDoArchiwum();
t.CommitUI();
}
session.Save();
// Odczyt stanu archiwizacji:
InformacjeOArchiwum status = pracownik.Archiwum.Status;
bool zanonimizowany = pracownik.Archiwum.Anonimizowany;
// Przywrócenie z archiwum:
using (var t = session.Logout(editMode: true))
{
var worker = new Pracownik.PrzywróćZArchiwumWorker { Pracownik = pracownik };
worker.PrzywróćZArchiwum();
t.CommitUI();
}
session.Save();
```
**Snippet (operacja seryjna — wielu pracowników):**
```csharp
var lista = session.GetKadry().Pracownicy
.Cast<Pracownik>()
.Where(p => /* kryterium */ true)
.ToArray();
using (var t = session.Logout(editMode: true))
{
var worker = new Pracownik.PrzenieśDoArchiwumLstWorker { Pracownicy = lista };
worker.PrzenieśDoArchiwum();
t.CommitUI();
}
session.Save();
```
**Pułapki:**
- **Brak publicznej metody na managerze** — nie szukaj `pracownik.Archiwum.Przenieś(...)`; jedyne
publiczne API zmiany to workery `PrzenieśDoArchiwumWorker`/`PrzywróćZArchiwumWorker`.
- Workery archiwizacji modyfikują dane → wywołuj w transakcji edycyjnej i `Save()`. Worker uruchamiany
„jak z UI" → `CommitUI()` (worker-extender §3, pkt 4).
- `Archiwum`, `ArchiwumInfo`, `InformacjeOArchiwum` służą **tylko do odczytu** stanu/historii archiwum.
- Pracownik z workera i `Pracownicy[]` muszą być z bieżącej sesji (safe-code §2.1).
- Archiwizacja bywa powiązana z anonimizacją (`Archiwum.Anonimizowany`) — to oddzielny stan; przeniesienie
do archiwum nie musi oznaczać anonimizacji.
### KADRY-A18 — Wyrejestrowanie / zwolnienie pracownika (★)
**Cel:** zakończyć zatrudnienie — ustawić rozwiązanie umowy (data, tryb, inicjatywa, podstawa prawna),
ewentualnie okres wypowiedzenia, oraz wygenerować wyrejestrowanie z ZUS (ZWUA) workerem.
**Publiczny kontrakt — `PracHistoria.Etat` (dane historyczne zatrudnienia):**
| Dana | Pole / typ | Uwaga |
|---|---|---|
| Koniec okresu zatrudnienia | `Etat.Okres: Soneta.Types.FromTo` | `Okres.To` = ostatni dzień zatrudnienia (zmiana „od daty" → KADRY-A14) |
| Rozwiązanie umowy (subrow) | `Etat.RozwiazanieUmowy: Soneta.Kadry.RozwiazanieUmowy` | zbiorczy subrow trybu zwolnienia |
| Inicjatywa zwolnienia | `Etat.RozwiazanieUmowy.Inicjatywa: KodInicjatywyZwolnienia` | enum |
| Kod zwolnienia (ZUS) | `Etat.RozwiazanieUmowy.KodZwolnienia: KodZwolnienia` | kod trybu rozwiązania |
| Podstawa prawna | `Etat.RozwiazanieUmowy.PodstawaPrawna: KodPodstawyPrawnejZwolnienia` | enum |
| Przyczyna rozwiązania | `Etat.RozwiazanieUmowy.PrzyczynaRozwUmowy: PrzyczynaRozwUmowy` | rekord słownika; opis: `PrzyczynaRozwUmowyOpis: string` |
| Za odszkodowaniem | `Etat.RozwiazanieUmowy.ZaOdszkodowaniem: bool` | — |
| Okres wypowiedzenia (subrow) | `Etat.OkresWypowiedzenia: Soneta.Kadry.OkresWypowiedzenia` | parametry wypowiedzenia |
| Długość wypowiedzenia | `Etat.OkresWypowiedzenia.Dni / .Tygodnie / .Miesiace: int` | składowe okresu |
| Data złożenia wypowiedzenia | `Etat.OkresWypowiedzenia.DataZlozenia: Date` | — |
| Skrócony okres | `Etat.OkresWypowiedzenia.Skrocony: bool` | — |
| Zwolnienie z obowiązku pracy od | `Etat.OkresWypowiedzenia.ZwolnionyZObowiazkuPracyOd: Date` | — |
| Data upływu wypowiedzenia | `Etat.OkresWypowiedzenia.Uplywa: Date` | wyliczana data rozwiązania (`DataRozwiązaniaUmowy` read-only) |
**Worker ZUS (wyrejestrowanie ZWUA):** `Soneta.Kadry.Pracownik.WyrejestrujPracownikaWorker`
(`[Action("Operacje seryjne/Wyrejestruj pracowników...")]`, metoda `Wyrejestruj()`):
| Składnik | Sygnatura | Uwaga |
|---|---|---|
| Ctor (parametry z `Context`) | `WyrejestrujPracownikaWorker(WyrejestrujPracownikaParams pars)` | `pars` inicjowany z `Context` |
| Data wyrejestrowania | `WyrejestrujPracownikaParams.Data: Date` | data zdarzenia ZWUA |
| Pracownicy | `Pracownicy: Pracownik[]` (`[Context]`) | lista do wyrejestrowania |
| Bieżąca data | `Current: Date` | data robocza |
| Akcja | `void Wyrejestruj()` | tworzy wyrejestrowania ZUS (ZWUA) |
**Snippet (ustawienie rozwiązania umowy — nowy zapis „od daty", KADRY-A14):**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
var dataRozwiazania = new Date(2026, 6, 30);
using (var t = session.Logout(editMode: true))
{
var ph = pracownik[dataRozwiazania]; // zapis obowiązujący na dzień rozwiązania (KADRY-A15)
var etat = ph.Etat;
// Zamknięcie okresu zatrudnienia ostatnim dniem pracy:
etat.Okres = new FromTo(etat.Okres.From, dataRozwiazania);
// Tryb rozwiązania (subrow RozwiazanieUmowy):
etat.RozwiazanieUmowy.Inicjatywa = KodInicjatywyZwolnienia.Pracownik;
etat.RozwiazanieUmowy.PodstawaPrawna = KodPodstawyPrawnejZwolnienia._550; // kody numeryczne wg słownika (NieDotyczy, _400.._463, _550)
// Opcjonalnie okres wypowiedzenia:
etat.OkresWypowiedzenia.DataZlozenia = new Date(2026, 5, 31);
etat.OkresWypowiedzenia.Miesiace = 1;
t.Commit();
}
session.Save();
```
**Snippet (wyrejestrowanie z ZUS — worker):**
```csharp
using (var t = session.Logout(editMode: true))
{
var pars = new Pracownik.WyrejestrujPracownikaWorker.WyrejestrujPracownikaParams(context)
{
Data = new Date(2026, 7, 1),
};
var worker = new Pracownik.WyrejestrujPracownikaWorker(pars)
{
Pracownicy = new[] { pracownik },
Current = Date.Today,
};
worker.Wyrejestruj();
t.CommitUI();
}
session.Save();
```
**Pułapki:**
- `RozwiazanieUmowy` i `OkresWypowiedzenia` to **subrowy** `Etat` — modyfikuj ich pola, nie przypisuj całych
obiektów.
- Konkretne wartości enumów (`KodInicjatywyZwolnienia`, `KodPodstawyPrawnejZwolnienia`, `PrzyczynaRozwUmowy`)
zależą od słownika danej bazy — w teście pobierz/odczytaj realne wartości z Demo zamiast zgadywać.
- `WyrejestrujPracownikaWorker` ma **konstruktor** przyjmujący `WyrejestrujPracownikaParams`, który z kolei
wymaga `Context` (`WyrejestrujPracownikaParams(Context cx)`) — worker jest praktycznie wywoływalny tylko
z dostępnym `Context`. Bez `Context` operację wyrejestrowania ZUS zrealizujesz tylko częściowo (samo
ustawienie `Etat.Okres`/`RozwiazanieUmowy` nie tworzy dokumentu ZWUA).
- `Uplywa`/`DataRozwiązaniaUmowy` bywają wyliczane — nie nadpisuj pól read-only.
- Zmiana warunków „od dnia" to nowy zapis (KADRY-A14); samo zamknięcie `Etat.Okres.To` na bieżącym zapisie jest
korektą całego okresu — używaj świadomie.
### KADRY-A19 — Przerejestrowanie pracownika (★)
**Cel:** zmienić kod tytułu ubezpieczenia (`Tyub4`) lub jednostkę (`Wydzial`) od konkretnego dnia —
co skutkuje **wyrejestrowaniem ze starym kodem i ponownym zgłoszeniem z nowym** (ZUS ZWUA + ZUA).
Realizacja: nowy zapis historii „od daty" (KADRY-A14) z innym `Etat.Ubezpieczenia.Tyub4`/`Etat.Wydzial`,
a generowanie deklaracji ZUS — workerem przerejestrowania.
**Publiczny kontrakt — pola do zmiany (na nowym zapisie `PracHistoria`):**
| Dana | Pole / typ | Uwaga |
|---|---|---|
| Tytuł ubezpieczenia | `Etat.Ubezpieczenia.Tyub4: Soneta.Kadry.TytulUbezpieczenia4` | rekord słownika; `session.GetKadry().TytulyUbezpiecz4.WgKodu[int]` (klucz `int`, np. `110`) |
| Jednostka organizacyjna | `Etat.Wydzial: Soneta.Kadry.Wydzial` | referencja do istniejącego wydziału |
**Worker ZUS (przerejestrowanie):** `Soneta.Deklaracje.UI.PrzerejestrowaniePracownikaWorker`
(`[Action("Przerejestrowanie pracownika …")]`, metoda `PrzerejestrowaniePracownika()`):
| Składnik (`PrzerejestrowaniePracownikaWorker.Params`) | Typ | Uwaga |
|---|---|---|
| `DataRejestracji` | `Soneta.Types.Date` | data ponownego zgłoszenia |
| `DataWypełnienia` | `Soneta.Types.Date` | data wypełnienia deklaracji |
| `Kedu` | `Soneta.Deklaracje.ZUS.KEDU` | zbiór deklaracji ZUS (KEDU) |
| `Przyczyna` | `Soneta.Kadry.Wyrejestrowanie` | przyczyna wyrejestrowania (do ZWUA) |
**Snippet (zmiana kodu tytułu ubezpieczenia / wydziału „od daty"):**
```csharp
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
var odDnia = new Date(2026, 7, 1);
using (var t = session.Logout(editMode: true))
{
// Nowy zapis historii „od daty" (KADRY-A14): Update klonuje + skraca poprzedni, AddRow dopina klon.
var nowy = (PracHistoria)pracownik.Historia.Update(odDnia);
pracownik.Module.PracHistorie.AddRow(nowy);
// Zmiana kodu tytułu ubezpieczenia (przerejestrowanie ubezpieczeniowe):
nowy.Etat.Ubezpieczenia.Tyub4 = session.GetKadry().TytulyUbezpiecz4.WgKodu[110];
// Lub/oraz zmiana jednostki organizacyjnej:
nowy.Etat.Wydzial = session.GetKadry().Wydzialy.Firma;
t.Commit();
}
session.Save();
```
**Pułapki:**
- Przerejestrowanie to **nowy zapis historii** (KADRY-A14: `Update(odDnia)` + `PracHistorie.AddRow(nowy)`) — nie
nadpisuj `Tyub4`/`Wydzial` na bieżącym zapisie (to zmieniłoby cały okres wstecz).
- `Tyub4` pobierasz ze słownika `TytulyUbezpiecz4` po **`int`** (`WgKodu[110]`), nie po stringu i nie „w locie".
- `Wydzial` to referencja do istniejącego wydziału (korzeń: `session.GetKadry().Wydzialy.Firma`).
- `PrzerejestrowaniePracownikaWorker` żyje w `Soneta.Deklaracje.UI` i jego `Params` wymaga m.in. `KEDU`
(zbiór deklaracji) oraz `Context` — generowanie ZWUA+ZUA jest realnie wykonalne tylko w środowisku z
`Context`/`KEDU`. Sama zmiana danych kadrowych (`Tyub4`/`Wydzial`) jest w pełni wykonalna publicznym API
bez workera; deklaracje ZUS — tylko przez worker UI.
- `Update(odDnia)` rzuca `DateDuplicateException`, jeśli na `odDnia` już zaczyna się zapis (KADRY-A14).