Files

16 KiB
Raw Permalink Blame History

KADRY06 — RCP — rejestracja czasu pracy

Wspólne fakty o typie, podstawowe typy i szablon wzorca: ../kadry.md.

Dwie tabele. Zarejestrowany (surowy) czas pracy z czytników RCP leży w pracownik.DniRCP : DateSubTable<Soneta.Kalend.DzienRCP> (tabela DniRCP). Pojedyncze zdarzenia wejścia/wyjścia (Soneta.Kalend.WejscieWyjscie, tabela WejsciaWyjscia) są childem DzienPracy (pole WejscieWyjscie.Dzien : DzienPracy, kolekcja dzienPracy.WeWy), a DzienPracy to ewidencja w pracownik.DniPracy. DzienRCP jest stanem zweryfikowanym RCP (z polem StanRCP : StanWeryfikacjiRCP), powstaje z importu/przeliczenia.

KADRY-F1 — Rejestracja czasu pracy pracownika (★)

Cel: odczytać zarejestrowany/zewidencjonowany czas pracy pracownika za dzień oraz (gdy trzeba) utworzyć dzień ewidencji.

Pola i typy:

Element Lokalizacja Typ Uwaga
Ewidencja (kolekcja) pracownik.DniPracy DateSubTable<Soneta.Kalend.DzienPracy> indeksator [Date] (get); element ctorem
Dzień ewidencji pracownik.DniPracy[data] Soneta.Kalend.DzienPracy null przy braku
RCP zweryfikowane (kolekcja) pracownik.DniRCP DateSubTable<Soneta.Kalend.DzienRCP> analogicznie
Dzień RCP pracownik.DniRCP[data] Soneta.Kalend.DzienRCP null przy braku
Przepracowany czas (subrow) DzienPracy.Praca / DzienRCP.Praca Soneta.Kalend.CzasPracy Praca.OdGodziny, Praca.DoGodziny, Praca.Czas : Time
Czas/Od (odczyt) Dzien*.Czas, Dzien*.OdGodziny Soneta.Types.Time kalkulowane
Stan weryfikacji RCP DzienRCP.StanRCP Soneta.Kalend.StanWeryfikacjiRCP zapisywalne
Flaga importu RCP Dzien*.RcpOK bool zapisywalne; stan rekordu po imporcie
Zdarzenia we/wy dnia DzienPracy.WeWy LpSubTable<Soneta.Kalend.WejscieWyjscie> patrz KADRY-F2
Uwagi / błędy (RCP) DzienRCP.Uwagi, DzienRCP.Bledy Soneta.Business.MemoText zapisywalne

Snippet:

var kalend = session.GetKadry().Session.GetKalend();
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
var data = new Date(2026, 6, 1);

// --- Odczyt zaewidencjonowanego czasu (ewidencja) ---
var dzienPracy = pracownik.DniPracy[data];          // typowane: DzienPracy lub null
if (dzienPracy is not null)
{
    Time przepracowano = dzienPracy.Praca.Czas;     // suma czasu pracy dnia
    Time od = dzienPracy.Praca.OdGodziny;
    Time @do = dzienPracy.Praca.DoGodziny;
}

// --- Odczyt stanu RCP (zweryfikowany rejestr) ---
var dzienRcp = pracownik.DniRCP[data];              // DzienRCP lub null
if (dzienRcp is not null)
{
    Time czasRcp = dzienRcp.Praca.Czas;
    StanWeryfikacjiRCP stan = dzienRcp.StanRCP;
}

// --- Utworzenie dnia ewidencji (gdy potrzebny ręczny wpis) ---
using (var t = session.Logout(editMode: true))
{
    var dp = pracownik.DniPracy[data];
    if (dp is null)
    {
        dp = session.AddRow(new DzienPracy(pracownik, data));   // ctor (Pracownik, Date)
        kalend.DniPracy.AddRow(dp);                              // alternatywnie przez Module.DniPracy
    }
    dp.Praca.OdGodziny = new Time(8, 0);
    dp.Praca.DoGodziny = new Time(16, 0);
    t.Commit();
}
session.Save();

Pułapki:

  • DniPracy/DniRCPtypowane (DateSubTable<DzienPracy> / <DzienRCP>) — indeksator [Date] zwraca od razu właściwy typ lub null. Nie iteruj całej kolekcji, by „znaleźć" dzień — użyj indeksatora po dacie albo [FromTo] dla zakresu.
  • Czas pracy ustawiaj na subrowie Praca (oddo); Dzien*.Czas/Dzien*.OdGodziny na rootcie dnia są kalkulowane (read-only).
  • DzienRCP to wynik weryfikacji importu RCP (z czytników) — w normalnym przepływie nie tworzysz go ręcznie, lecz odczytujesz po imporcie/przeliczeniu. DzienPracy (ewidencja) to właściwe miejsce na ręczny wpis.
  • Świeży DzienPracy z new DzienPracy(pracownik, data) trzeba dodać do tabeli (Module.DniPracy.AddRow(...) lub session.AddRow(...)) — sam ctor go nie rejestruje.

KADRY-F2 — Rejestracja wejścia/wyjścia (RCP) (★)

Cel: dodać zdarzenie wejścia/wyjścia do dnia oraz odczytać listę zdarzeń RCP danego dnia.

Pola i typy (Soneta.Kalend.WejscieWyjscie, tabela WejsciaWyjscia):

Pole Typ Uwaga
Dzien Soneta.Kalend.DzienPracy właściciel (guided-parent); ustawiany przez ctor (DzienPracy)
Godzina Soneta.Types.Time godzina zdarzenia (zapisywalne)
Typ Soneta.Kalend.TypWejsciaWyjscia enum: Niezdefiniowany, Wejscie, Wyjscie, WejscieSluzbowe, WyjscieSluzbowe, WejsciePrywatne, WyjsciePrywatne
Operacja int kod operacji urządzenia (zapisywalne)
Lp int liczba porządkowa zdarzeń w dniu (bazodanowe)
DefinicjaZdarzenia Soneta.Kalend.DefinicjaZdarzeniaRCP opcjonalna definicja zdarzenia ze słownika DefZdarzenRCP
Kolekcja zdarzeń dnia DzienPracy.WeWy : LpSubTable<WejscieWyjscie> uporządkowana po Lp

Snippet:

var kalend = session.GetKadry().Session.GetKalend();
var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
var data = new Date(2026, 6, 1);

using (var t = session.Logout(editMode: true))
{
    // Upewnij się, że istnieje dzień ewidencji (właściciel zdarzeń):
    var dp = pracownik.DniPracy[data];
    if (dp is null)
    {
        dp = session.AddRow(new DzienPracy(pracownik, data));
        kalend.DniPracy.AddRow(dp);
    }

    // Wejście 8:00
    var we = new WejscieWyjscie(dp);          // ctor wiąże zdarzenie z dniem
    kalend.WejsciaWyjscia.AddRow(we);
    we.Godzina = new Time(8, 0);
    we.Typ = TypWejsciaWyjscia.Wejscie;

    // Wyjście 16:00
    var wy = new WejscieWyjscie(dp);
    kalend.WejsciaWyjscia.AddRow(wy);
    wy.Godzina = new Time(16, 0);
    wy.Typ = TypWejsciaWyjscia.Wyjscie;

    t.Commit();
}
session.Save();

// --- Odczyt zdarzeń dnia ---
var dzien = pracownik.DniPracy[data];
if (dzien is not null)
{
    foreach (WejscieWyjscie wewy in dzien.WeWy)   // posortowane po Lp
    {
        // wewy.Godzina, wewy.Typ, wewy.Operacja
    }
}

Pułapki:

  • WejscieWyjscie jest childem DzienPracy, nie DzienRCP — najpierw potrzebujesz dnia ewidencji (pracownik.DniPracy[data]); zdarzenia wiążesz ctorem new WejscieWyjscie(dzienPracy) i dodajesz do tabeli kalend.WejsciaWyjscia.AddRow(...).
  • Typ to enum TypWejsciaWyjscia (Wejscie/Wyjscie/…), nie string ani int. Para wejście+wyjście jest podstawą wyliczenia czasu dnia z surowych zdarzeń.
  • DefinicjaZdarzenia jest opcjonalna — przy ręcznym wpisie wystarczą Godzina + Typ. Jeśli używasz definicji, pobierz wpis ze słownika konfiguracyjnego kalend.DefZdarzenRCP (nie twórz w locie).
  • WeWy to LpSubTable — kolejność zdarzeń wynika z Lp (nadawane automatycznie); nie ustawiaj Lp ręcznie. Do usunięcia wszystkich zdarzeń dnia (przy ponownym imporcie) służy kasowanie elementów kolekcji.
  • Surowe zdarzenia są przeliczane na czas pracy/RCP przez kalkulator i import — samo dodanie wejść/wyjść nie aktualizuje automatycznie DzienRCP (to robi przeliczenie/import RCP).

KADRY-F3 — Import danych z RCP (bezpośredni i przez tabelę pośrednią)

Cel: wczytać surowe odbicia z czytników RCP i przeliczyć je na ewidencję/zweryfikowany RCP. UWAGA: operacja plikowa/sieciowa — opis modelu; samego importu z pliku/urządzenia NIE testujemy.

Model danych:

Element Lokalizacja Typ Uwaga
Tabela pośrednia (surowe odbicia) DzienPracy.WeWy LpSubTable<Soneta.Kalend.WejscieWyjscie> zdarzenia we/wy (godzina, typ) — patrz KADRY-F2
Zarejestrowany RCP (zweryfikowany) pracownik.DniRCP[data] Soneta.Kalend.DzienRCP wynik importu/przeliczenia
Ewidencja pracownik.DniPracy[data] Soneta.Kalend.DzienPracy docelowa realizacja
Flaga importu DzienPracy.RcpOK / DzienRCP.RcpOK bool „stan rekordu po imporcie z RCP"
Stan weryfikacji DzienRCP.StanRCP StanWeryfikacjiRCP patrz KADRY-F4

Workery przeliczające (po wczytaniu odbić — operują na obiektach sesji):

Worker Sygnatura Rola
Soneta.Kalend.ImportDniaWorker ctor (), DzienPracy DzienPracy {get;set;}, void Przelicz() przelicza pojedynczy dzień z we/wy na czas pracy
Soneta.Kalend.RCPWeryfikatorWorker Dopasuj(), DopasujDlaZaznaczonych(), Dodaj(), Usun() (+ IsVisible*), props rw : RCPWeryfikator, Strefy, Wybrana dopasowanie odbić do plan/strefy (UI)

Snippet (przeliczenie dnia z już wczytanych we/wy — bez pliku):

var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
var dzien = pracownik.DniPracy[new Date(2026, 6, 1)];   // dzień z wpisanymi WeWy (KADRY-F2)
if (dzien is not null)
{
    using (var t = session.Logout(editMode: true))
    {
        new ImportDniaWorker { DzienPracy = dzien }.Przelicz();   // we/wy -> czas pracy
        t.Commit();
    }
    session.Save();
}

Pułapki / wykonalność:

  • Sam import z pliku/urządzenia (czytnik, sieć, format) jest poza zakresem testu — wymaga zewnętrznego źródła (plik/serwis), brak czystego API w tym kontrakcie.
  • Testowalny fragment: przygotowanie tabeli pośredniej DzienPracy.WeWy (ctor WejscieWyjscie(dp), patrz KADRY-F2) + ImportDniaWorker.Przelicz() — to przelicza już-wczytane odbicia bez I/O.
  • RCPWeryfikatorWorker jest mocno UI (metody IsVisible*, Strefy/Wybrana) — to dopasowanie ręczne; nie wywoływać z kodu biznesowego.
  • DzienRCP powstaje z importu/przeliczenia — w teście nie twórz go „z palca"; odczytuj po Przelicz().

KADRY-F4 — Weryfikacja i korekta danych RCP (★ testowalne)

Cel: odczytać i skorygować zweryfikowany rekord RCP — zmienić stan weryfikacji oraz poprawić godziny pracy / opisać błędy i uwagi.

Pola Soneta.Kalend.DzienRCP (tabela DniRCP, child Pracownik):

Pole Typ Rodzaj Uwaga
Data Soneta.Types.Date bazodanowe data dnia (ctor)
Pracownik Soneta.Kadry.Pracownik bazodanowe, guided-parent właściciel
Praca Soneta.Kalend.CzasPracy bazodanowe Praca.OdGodziny/Praca.DoGodziny/Praca.Czas : Time (zapisywalne)
Czas, OdGodziny Soneta.Types.Time kalkulowane read-only (z Praca)
StanRCP Soneta.Kalend.StanWeryfikacjiRCP bazodanowe stan weryfikacji (zapisywalne)
RcpOK bool bazodanowe stan rekordu po imporcie (zapisywalne)
Uwagi Soneta.Business.MemoText bazodanowe uwagi do weryfikacji
Bledy Soneta.Business.MemoText bazodanowe opis błędów
Strefy SubTable<Soneta.Kalend.StrefaRCP> strefy zarejestrowane
StrefyOrg Soneta.Business.MemoText bazodanowe strefy źródłowe (org.)

Soneta.Kalend.StanWeryfikacjiRCP (enum): DoWeryfikacji, WymagaWeryfikacji, PrzekazanyDoWyjaśnienia, DoZatwierdzenia, Modyfikowany, Naniesiony, Poprawny, Błędny, Wszystkie.

Snippet (korekta godzin + zmiana stanu):

var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];
var dzienRcp = pracownik.DniRCP[new Date(2026, 6, 1)];   // DzienRCP lub null
if (dzienRcp is not null)
{
    using (var t = session.Logout(editMode: true))
    {
        dzienRcp.Praca.OdGodziny = new Time(8, 0);       // korekta na subrowie Praca
        dzienRcp.Praca.DoGodziny = new Time(16, 0);
        dzienRcp.StanRCP = StanWeryfikacjiRCP.Poprawny;  // zatwierdzenie weryfikacji
        dzienRcp.Uwagi = (MemoText)"Skorygowano wyjście";
        t.Commit();
    }
    session.Save();
}

Pułapki:

  • DniRCP jest typowane (DateSubTable<DzienRCP>) — indeksator [Date] zwraca DzienRCP/null; do zakresu użyj [FromTo]. Nie iteruj kolekcji w poszukiwaniu dnia.
  • Godziny koryguj na subrowie Praca (Praca.OdGodziny/DoGodziny); DzienRCP.Czas/OdGodziny na rootcie są kalkulowane (read-only).
  • StanRCP to enum StanWeryfikacjiRCP — nie string. Zmiana stanu może podlegać weryfikatorom.
  • W Demo DzienRCP istnieje tylko gdy był import/przeliczenie — test korekty zakłada istniejący dzień (sprawdzaj is not null), nie twórz DzienRCP ręcznie.

KADRY-F5 — Rozliczenie pracy hybrydowej / aktualizacja podzielników na podstawie pracy hybrydowej

Cel: rozliczyć czas pracy hybrydowej (podział na strefy: stacjonarna / praca zdalna / zdalna okazjonalna) i zaktualizować podzielniki (elementy rozliczenia czasu pracy / strefy dnia), na podstawie których naliczane są składniki płacowe i koszty.

Model danych (publiczny kontrakt):

Element Lokalizacja Typ Uwaga
Strefy pracy dnia DzienPracy.Strefy SubTable<Soneta.Kalend.StrefaPracy> podział dnia na strefy
Strefa pracy Soneta.Kalend.StrefaPracy (ctor (DzienPracy dzien)) Definicja : DefinicjaStrefy, CzasRozliczany : Time, OdGodziny/Czas (kalk.)
Definicja strefy Soneta.Kalend.DefinicjaStrefy konfiguracja Typ : TypStrefy, Wchodzi, Rozliczana; stałe Praca_Zdalna, PracaZdalnaOkazjonalna : Guid
Dokumenty rozliczenia pracownik.RozliczeniaCzasuPracy SubTable<Soneta.Kalend.RozliczenieCzasuPracy> dokumenty rozliczenia czasu (podzielniki)
Elementy rozliczenia pracownik.ElementyRozliczeniaCzasuPracy SubTable<Soneta.Kalend.ElementRozliczeniaCzasuPracy> pozycje podzielnika
Dokument rozliczenia Soneta.Kalend.RozliczenieCzasuPracy (ctor (Pracownik, DefinicjaRozliczeniaCzasuPracy)) root Data, Seria, Stan : StanyRozliczeniaCzasuPracy
Pozycja rozliczenia Soneta.Kalend.ElementRozliczeniaCzasuPracy (ctor (RozliczenieCzasuPracy dokument)) child Definicja : DefinicjaStrefy, Data, OdGodziny, Czas, CzasPozostały/CzasDostępny/Zrealizowane (kalk.)

Soneta.Kalend.TypStrefy (enum): NieWplywa, Zwieksza, Zmniejsza.

Workery (UI/extendery — praca zdalna/hybrydowa):

  • Soneta.Kalend.StrefaPracy.PracaZdalnaWorker (Strefa : StrefaPracy) — oznaczenie strefy jako praca zdalna.
  • Soneta.Kadry.PracaZdalna.DzienStrefaExtWorker, ElementRozliczeniaCzasuPracyExtWorker, DzienZestawienieExtender — extendery zestawień/dni dla pracy zdalnej.

Snippet (odczyt rozkładu na strefy + dokument rozliczenia):

var pracownik = session.GetKadry().Pracownicy.WgKodu["006"];

// Odczyt podziału dnia na strefy (stacjonarna / zdalna):
var dzien = pracownik.DniPracy[new Date(2026, 6, 1)];
if (dzien is not null)
{
    foreach (StrefaPracy s in dzien.Strefy)
    {
        DefinicjaStrefy def = s.Definicja;   // strefa (np. praca zdalna)
        Time rozliczany = s.CzasRozliczany;  // czas rozliczany w strefie
    }
}

// Pozycje podzielnika (elementy rozliczenia czasu pracy):
foreach (ElementRozliczeniaCzasuPracy el in pracownik.ElementyRozliczeniaCzasuPracy)
{
    DefinicjaStrefy def = el.Definicja;
    Time czas = el.Czas;
}

Pułapki / wykonalność:

  • Rozkład pracy hybrydowej to strefy (DzienPracy.Strefy / DefinicjaStrefy z flagą zdalna) + dokument RozliczenieCzasuPracy z pozycjami ElementRozliczeniaCzasuPracy (podzielniki).
  • RozliczenieCzasuPracy to root (ctor (Pracownik, DefinicjaRozliczeniaCzasuPracy)) — utworzenie wymaga istniejącej DefinicjaRozliczeniaCzasuPracy z konfiguracji; pozycje ctorem new ElementRozliczeniaCzasuPracy(dokument). Czysty odczyt jest bezpieczny i bez transakcji.
  • Aktualizacja podzielników na podstawie pracy hybrydowej przebiega głównie przez extendery/UI (DzienStrefaExtWorker, ElementRozliczeniaCzasuPracyExtWorker, PracaZdalnaWorker) zależne od Context/wniosków e-pracownika — brak prostego, czystego API operacyjnego.
  • Wymaga skonfigurowanych DefinicjaStrefy (Praca_Zdalna / PracaZdalnaOkazjonalna) — w Demo strefy mogą nie być włączone, co czyni budowę rozliczenia kruchą do testu (raczej odczyt niż zapis).