using System;
using System.Linq;
using AwesomeAssertions;
using NUnit.Framework;
using Soneta.Kadry;
using Soneta.Place;
using Soneta.Types;
using Prac = Soneta.Kadry.Pracownik;
namespace Soneta.Skills.Test.KadryPlace.Pracownik;
///
/// Rozdział G (reszta) — „Umowy cywilnoprawne" (receptury G3, G4, G5).
///
/// Testy są wykonywalną dokumentacją publicznego kontraktu platformy Soneta dla operacji na
/// umowach cywilnoprawnych: operacja seryjna „Dodaj umowy" dla grupy osób (G3), rachunek/rozliczenie
/// umowy = wypłata WyplataUmowa naliczana mechanizmem płac (G4), oraz zgłoszenia ZUS
/// zleceniobiorców na podstawie schematu ubezpieczeń umowy (G5).
///
///
/// Wszystko działa na bazie Demo (GoldStandard) z automatycznym rollbackiem po teście. Pracownicy
/// etatowi z Demo (kody "006".."039") nie mają jeszcze umów cywilnoprawnych — czysty punkt wejścia.
/// Operujemy wyłącznie na publicznym kontrakcie — tak jak dodatek programisty zewnętrznego bez
/// dostępu do kodu źródłowego aplikacji.
///
///
[TestFixture]
public class RozdzialGrest_UmowyTest : PracownikTestBase
{
// Pobranie definicji elementu = rodzaju umowy ze słownika konfiguracyjnego po stałej Guid.
private DefinicjaElementu DefUmowy(Guid rodzaj) =>
Place.DefElementow[rodzaj] as DefinicjaElementu;
// Dobiera datę mieszczącą się w okresie aktywnego etatu pracownika (jak w H): koniec miesiąca
// rozpoczęcia etatu, ograniczony do [From, To]. Etaty Demo są zwykle otwarte (To = MaxValue).
private static Date DataWEtacie(Prac pracownik)
{
var okres = pracownik.Last.Etat.Okres;
var from = okres.From;
var koniecMiesiaca = new Date(from.Year, from.Month, 1).AddMonths(1).AddDays(-1);
if (koniecMiesiaca < from) koniecMiesiaca = from;
if (okres.To != Date.MaxValue && koniecMiesiaca > okres.To) koniecMiesiaca = okres.To;
return koniecMiesiaca;
}
// ====================== G3 — Operacja seryjna „Dodaj umowy" dla grupy osób ======================
[Test]
[Description("G3 (wariant B - petla, jak G1): operacja seryjna 'Dodaj umowy' = G1 powtorzone dla " +
"każdej osoby z grupy. Dla każdego pracownika tworzymy Session.AddRow(new Umowa(p)) " +
"z tymi samymi danymi nagłówkowymi (Element, Okres, RodzajRozliczenia, TypWartosci, " +
"Wydzial) i kwotą na umowa.Last.Wartosc. Każda osoba dostaje osobny rekord Umowa.")]
public void G3_DodajUmowySeryjnie_PetlaPoGrupie_TworzyUmoweKazdejOsobie()
{
var defZlecenie = DefUmowy(DefinicjaElementu.UmowaZlecenie);
defZlecenie.Should().NotBeNull("baza Demo zawiera definicję umowy zlecenie");
var okres = new FromTo(new Date(2026, 1, 1), new Date(2026, 12, 31));
var kody = new[] { Pracownik_.Andrzejewski, Pracownik_.Bednarek, Pracownik_.Bujak };
var guidy = new Guid[kody.Length];
InTransaction(() =>
{
for (int i = 0; i < kody.Length; i++)
{
var p = Pracownik(kody[i]);
p.Should().NotBeNull();
p.Umowy.Cast().Should().BeEmpty("pracownik Demo nie ma jeszcze umów");
// Jawne tworzenie jak w G1 — operacja seryjna to to samo powtórzone w pętli.
var umowa = Session.AddRow(new Umowa(p));
umowa.Element = defZlecenie;
umowa.Data = okres.From;
umowa.Okres = okres;
umowa.Tytul = "Umowa zlecenie - projekt grupowy";
umowa.RodzajRozliczenia = RodzajeRozliczeniaUmowy.KwotaDoWypłaty;
umowa.TypWartosci = TypWartosciUmowy.Brutto;
umowa.Wydzial = Kadry.Wydzialy.Firma; // jednostka organizacyjna wymagana przy Save
umowa.Last.Wartosc = new Currency(4000m); // kwota na zapisie historycznym
guidy[i] = p.Guid;
}
});
SaveDispose();
// Każda osoba z grupy ma teraz jedną umowę o tych samych danych nagłówkowych.
foreach (var g in guidy)
{
var u = Get(g).Umowy.Cast().Single();
// Element to definicja konfiguracyjna — po SaveDispose porównujemy po Guid (inna instancja).
u.Element.Guid.Should().Be(defZlecenie.Guid);
u.Element.RodzajZrodla.Should().Be(RodzajŹródłaWypłaty.Umowa);
u.Tytul.Should().Be("Umowa zlecenie - projekt grupowy");
u.RodzajRozliczenia.Should().Be(RodzajeRozliczeniaUmowy.KwotaDoWypłaty);
u.Okres.From.Should().Be(okres.From);
u.Last.Wartosc.Should().Be(new Currency(4000m));
}
}
[Test]
[Description("G3 (wariant A — worker platformy): Pracownik.DodajUmowęWorker (DataType Pracownik, " +
"ctor przyjmuje Session) z ustawionymi Pracownicy (grupa) i Pars " +
"(DodajUmowęWorker.Params(Context): Element, Okres, Data, Tytuł, RodzajRozliczenia, " +
"TypWartości, Wartość, Wydział). Akcja DodajUmowę() (void) tworzy umowę każdej osobie.")]
public void G3_DodajUmowyWorker_TworzyUmoweKazdejZaznaczonejOsobie()
{
var defZlecenie = DefUmowy(DefinicjaElementu.UmowaZlecenie);
var okres = new FromTo(new Date(2026, 1, 1), new Date(2026, 12, 31));
var osoby = new[]
{
Pracownik(Pracownik_.Andrzejewski),
Pracownik(Pracownik_.Bednarek),
Pracownik(Pracownik_.Bujak),
};
var guidy = osoby.Select(p => p.Guid).ToArray();
foreach (var p in osoby)
p.Umowy.Cast().Should().BeEmpty("pracownik Demo nie ma jeszcze umów");
// Parametry operacji seryjnej — Params(Context) (ContextBase), pola z diakrytykami.
var pars = new Prac.DodajUmowęWorker.Params(Context);
pars.Element = defZlecenie;
pars.Okres = okres;
pars.Data = okres.From;
pars.Tytuł = "Umowa zlecenie - operacja seryjna";
pars.RodzajRozliczenia = RodzajeRozliczeniaUmowy.KwotaDoWypłaty;
pars.TypWartości = TypWartosciUmowy.Brutto;
pars.Wartość = new Currency(3500m);
pars.Wydział = Kadry.Wydzialy.Firma; // wymagany
// Worker przyjmuje Session w konstruktorze; Pracownicy = grupa z zaznaczenia.
var worker = new Prac.DodajUmowęWorker(Session) { Pracownicy = osoby, Pars = pars };
worker.DodajUmowę(); // void — tworzy umowy wszystkim Pracownicy
SaveDispose();
// Każda osoba dostała umowę o danych z Pars.
foreach (var g in guidy)
{
var u = Get(g).Umowy.Cast().Single();
u.Element.Guid.Should().Be(defZlecenie.Guid); // porównanie po Guid (inna instancja)
u.Tytul.Should().Be("Umowa zlecenie - operacja seryjna");
u.Okres.From.Should().Be(okres.From);
u.Last.Wartosc.Should().Be(new Currency(3500m));
}
}
// ====================== G4 — Rachunek do umowy (rozliczenie = WyplataUmowa) ======================
[Test]
[Description("G4: 'rachunek do umowy zlecenia' = wyplata WyplataUmowa naliczana mechanizmem plac " +
"(jak H2), NIE rekord w pracownik.Rachunki (to rachunki bankowe). Tworzymy umowę " +
"(G1), potem new NaliczanieSeryjne.Umowy(new UmowaParams(Context)) { Umowa = u }." +
"Nalicz(); wynik to WyplataUmowa (Typ == Umowa). Stan rozliczenia: Umowa.Stan, " +
"Umowa.Splacono, Umowa.Pozostało.")]
public void G4_RachunekDoUmowy_NaliczanieTworzyWyplateUmowa_IZmieniaStan()
{
var pracownik = Pracownik(Pracownik_.Strzelecki);
pracownik.Should().NotBeNull();
var data = DataWEtacie(pracownik);
var okresUmowy = new FromTo(new Date(data.Year, data.Month, 1), data);
// 1) Umowa zlecenie (jak G1) — dane operacyjne tworzymy w trybie edycji.
Guid guidUmowy = Guid.Empty;
InTransaction(() =>
{
var u = Session.AddRow(new Umowa(pracownik));
u.Element = DefUmowy(DefinicjaElementu.UmowaZlecenie);
u.Data = okresUmowy.From;
u.Okres = okresUmowy;
u.Tytul = "Umowa zlecenie - rachunek G4";
u.RodzajRozliczenia = RodzajeRozliczeniaUmowy.KwotaDoWypłaty;
u.TypWartosci = TypWartosciUmowy.Brutto;
u.Wydzial = Kadry.Wydzialy.Firma;
u.Last.Wartosc = new Currency(3000m);
guidUmowy = u.Guid;
});
SaveDispose();
var umowa = Get(guidUmowy);
// Przed rozliczeniem umowa jest niewypłacona.
umowa.Stan.Should().Be(StanUmowy.Niewypłacona, "świeżo dodana umowa nie ma rachunku");
// 2) Rachunek = naliczenie wypłaty z umowy (jak H2). UmowaParams NIE ustawia Naliczanie.
var pars = new NaliczanieSeryjne.UmowaParams(Context);
pars.DataWypłaty = data;
pars.DataListy = pars.DataWypłaty;
// Ustawienie Umowa nadpisuje Pracownik właścicielem umowy. Nalicz() commituje sam.
var naliczanie = new NaliczanieSeryjne.Umowy(pars) { Umowa = umowa };
NaliczanieWypłat wynik = naliczanie.Nalicz();
var wyplaty = wynik.WszystkieWypłaty.Cast().ToList();
wyplaty.Should().NotBeEmpty("naliczenie umowy tworzy rachunek (WyplataUmowa)");
var w = wyplaty[0];
w.Typ.Should().Be(TypWyplaty.Umowa, "rachunek do umowy to wypłata typu Umowa");
w.Should().BeAssignableTo("rachunek to konkretny typ WyplataUmowa");
((WyplataUmowa)w).Umowa.Guid.Should().Be(umowa.Guid, "WyplataUmowa wskazuje swoją umowę");
SaveDispose();
// 3) Stan rozliczenia umowy po wystawieniu rachunku.
var umowa2 = Get(guidUmowy);
umowa2.Stan.Should().NotBe(StanUmowy.Niewypłacona,
"po naliczeniu rachunku umowa nie jest już całkowicie niewypłacona");
umowa2.Splacono.Value.Should().BeGreaterThan(0m, "część/całość kwoty została rozliczona");
// Splacono + Pozostało odpowiada modelowi rozliczenia (kwoty Currency).
(umowa2.Splacono.Value + umowa2.Pozostało.Value).Should().BeGreaterThanOrEqualTo(0m);
}
[Test]
[Description("G4 (odczyt): rachunki (wypłaty) wystawione do umowy odczytujemy przez " +
"pracownik.Wyplaty.OfType().Where(x => x.Umowa == umowa); składniki " +
"rachunku to WypElement (Wartosc). pracownik.Rachunki to rachunki BANKOWE — nie umowy.")]
public void G4_OdczytRachunkowUmowy_PrzezWyplatyUmowa()
{
var pracownik = Pracownik(Pracownik_.Andrzejewski);
var data = DataWEtacie(pracownik);
var okresUmowy = new FromTo(new Date(data.Year, data.Month, 1), data);
Guid guidUmowy = Guid.Empty;
InTransaction(() =>
{
var u = Session.AddRow(new Umowa(pracownik));
u.Element = DefUmowy(DefinicjaElementu.UmowaZlecenie);
u.Data = okresUmowy.From;
u.Okres = okresUmowy;
u.Tytul = "Umowa zlecenie - odczyt rachunków";
u.RodzajRozliczenia = RodzajeRozliczeniaUmowy.KwotaDoWypłaty;
u.TypWartosci = TypWartosciUmowy.Brutto;
u.Wydzial = Kadry.Wydzialy.Firma;
u.Last.Wartosc = new Currency(2500m);
guidUmowy = u.Guid;
});
SaveDispose();
var umowa = Get(guidUmowy);
var pars = new NaliczanieSeryjne.UmowaParams(Context);
pars.DataWypłaty = data;
pars.DataListy = pars.DataWypłaty;
new NaliczanieSeryjne.Umowy(pars) { Umowa = umowa }.Nalicz();
SaveDispose();
var pracownik2 = Pracownik(Pracownik_.Andrzejewski);
var umowa2 = Get(guidUmowy);
// Rachunki = wypłaty z umowy filtrowane po umowie (po Guid, bo różne instancje Row).
var rachunki = pracownik2.Wyplaty.OfType()
.Where(x => x.Umowa != null && x.Umowa.Guid == umowa2.Guid)
.ToList();
rachunki.Should().NotBeEmpty("wystawiliśmy rachunek do umowy");
foreach (var r in rachunki)
foreach (WypElement e in r.Elementy)
e.Definicja.Should().NotBeNull("każdy składnik rachunku ma definicję elementu");
// Składniki naliczone bezpośrednio z umowy (Umowa.Elementy).
umowa2.Elementy.Cast().Should().NotBeEmpty(
"naliczony rachunek wiąże składniki z umową (Umowa.Elementy)");
}
// ====================== G5 — Zgłoszenia ZUS zleceniobiorców (ZUA / ZZA / ZWUA) ======================
[Test]
[Description("G5 (schemat ubezpieczeń): typ zgłoszenia (ZUA vs ZZA) wynika ze schematu " +
"UmowaHistoria.Ubezpieczenia (umowa.Last.Ubezpieczenia), nie z parametru workera. " +
"ZUA = społeczne obowiązkowe (Emerytalne/Rentowe) + zdrowotne; Tyub4 pobierany ze " +
"słownika konfiguracyjnego Kadry.TytulyUbezpiecz4. Spoleczne.Od jest read-only — " +
"datę objęcia ustawiamy zbiorczo przez Ubezpieczenia.ObowiazkoweOd.")]
public void G5_SchematUbezpieczenUmowy_ZUA_SpoleczneObowiazkoweIZdrowotne()
{
var pracownik = Pracownik(Pracownik_.Bednarek);
var okres = new FromTo(new Date(2026, 1, 1), new Date(2026, 12, 31));
// Tytuł ubezpieczenia zleceniobiorcy pobieramy DYNAMICZNIE ze słownika (nie tworzymy w locie).
var tyub4 = Kadry.TytulyUbezpiecz4.Cast().FirstOrDefault();
tyub4.Should().NotBeNull("baza Demo zawiera słownik tytułów ubezpieczenia (TytulyUbezpiecz4)");
Guid guidUmowy = Guid.Empty;
InTransaction(() =>
{
var u = Session.AddRow(new Umowa(pracownik));
u.Element = DefUmowy(DefinicjaElementu.UmowaZlecenie);
u.Data = okres.From;
u.Okres = okres;
u.Tytul = "Umowa zlecenie - ZUA";
u.RodzajRozliczenia = RodzajeRozliczeniaUmowy.KwotaDoWypłaty;
u.TypWartosci = TypWartosciUmowy.Brutto;
u.Wydzial = Kadry.Wydzialy.Firma;
u.Last.Wartosc = new Currency(4000m);
// Schemat ubezpieczeń umowy (historyczny) — ZUA: społeczne obowiązkowe + zdrowotne.
var ub = u.Last.Ubezpieczenia;
ub.Tyub4 = tyub4;
ub.ObowiazkoweOd = okres.From; // data objęcia społecznymi obowiązkowymi
ub.Emerytalne.Obowiazkowe = true;
ub.Rentowe.Obowiazkowe = true;
ub.Zdrowotne.ObowiazkoweOd = okres.From; // na Zdrowotne ObowiazkoweOd jest zapisywalne
guidUmowy = u.Guid;
});
SaveDispose();
var umowa = Get(guidUmowy);
var ub2 = umowa.Last.Ubezpieczenia;
ub2.Tyub4.Should().NotBeNull("tytuł ubezpieczenia zapisany na schemacie umowy");
ub2.Emerytalne.Obowiazkowe.Should().BeTrue("ZUA: społeczne obowiązkowe (emerytalne)");
ub2.Rentowe.Obowiazkowe.Should().BeTrue("ZUA: społeczne obowiązkowe (rentowe)");
ub2.Zdrowotne.ObowiazkoweOd.Should().Be(okres.From, "ZUA obejmuje też zdrowotne");
// Schemat ubezpieczeń umowy leży na zapisie historycznym (delegat umowa.Ubezpieczenia).
umowa.Ubezpieczenia.Should().NotBeNull("Umowa.Ubezpieczenia to delegat do Last.Ubezpieczenia");
}
[Test]
[Description("G5 (ZZA): zleceniobiorca podlegający TYLKO zdrowotnemu (np. uczeń/student/zbieg " +
"tytułów) → ZZA. Na schemacie UmowaHistoria.Ubezpieczenia zostawiamy Emerytalne/" +
"Rentowe.Obowiazkowe = false, ustawiamy tylko Zdrowotne.ObowiazkoweOd.")]
public void G5_SchematUbezpieczenUmowy_ZZA_TylkoZdrowotne()
{
var pracownik = Pracownik(Pracownik_.Bujak);
var okres = new FromTo(new Date(2026, 1, 1), new Date(2026, 6, 30));
var tyub4 = Kadry.TytulyUbezpiecz4.Cast().FirstOrDefault();
Guid guidUmowy = Guid.Empty;
InTransaction(() =>
{
var u = Session.AddRow(new Umowa(pracownik));
u.Element = DefUmowy(DefinicjaElementu.UmowaZlecenie);
u.Data = okres.From;
u.Okres = okres;
u.Tytul = "Umowa zlecenie - ZZA";
u.RodzajRozliczenia = RodzajeRozliczeniaUmowy.KwotaDoWypłaty;
u.TypWartosci = TypWartosciUmowy.Brutto;
u.Wydzial = Kadry.Wydzialy.Firma;
u.Last.Wartosc = new Currency(2000m);
var ub = u.Last.Ubezpieczenia;
ub.Tyub4 = tyub4;
// ZZA: brak społecznych obowiązkowych, tylko zdrowotne.
// UWAGA: domyślnie umowa zlecenie ma Emerytalne/Rentowe.Obowiazkowe = true (schemat ZUA);
// dla ZZA trzeba je JAWNIE wyłączyć — samo ustawienie zdrowotnego nie wystarcza.
ub.Emerytalne.Obowiazkowe = false;
ub.Rentowe.Obowiazkowe = false;
ub.Zdrowotne.ObowiazkoweOd = okres.From;
guidUmowy = u.Guid;
});
SaveDispose();
var ub2 = Get(guidUmowy).Last.Ubezpieczenia;
ub2.Emerytalne.Obowiazkowe.Should().BeFalse("ZZA: brak społecznych obowiązkowych (emerytalne)");
ub2.Rentowe.Obowiazkowe.Should().BeFalse("ZZA: brak społecznych obowiązkowych (rentowe)");
ub2.Zdrowotne.ObowiazkoweOd.Should().Be(okres.From, "ZZA: tylko zdrowotne");
}
[Test]
[Ignore("Generowanie zgłoszenia ZUA/ZZA workerem ZarejestrujUmowyWorker.Rejestracja wymaga " +
"kompletnej konfiguracji płatnika/KEDU i kontekstu deklaracji ZUS, niedostępnego w " +
"izolowanym środowisku testów Demo (bez sieci). Dokumentujemy kontrakt workera bez " +
"uruchamiania generowania (ZarejestrujUmowy() / WyrejestrujUmowy()).")]
[Description("G5 (worker — kontrakt): Soneta.Deklaracje.ZUS.ZarejestrujUmowyWorker (DataType " +
"Umowa, ctor bezparametrowy, Umowy: Umowa[]). Zgłoszenie: zagnieżdżona Rejestracja " +
"(Pars: ParamsZ — Okres, DataDokumentu, DataWypełnienia, ZarejestrujRodzinę) i akcja " +
"ZarejestrujUmowy(): object generująca ZUA/ZZA wg schematu ubezpieczeń umowy. " +
"Wyrejestrowanie analogicznie WyrejestrujUmowy() → ZWUA. KEDU/wysyłka → sieć.")]
public void G5_ZgloszenieZUS_Worker_KontraktBezGenerowania()
{
var pracownik = Pracownik(Pracownik_.Bednarek);
var okres = new FromTo(new Date(2026, 1, 1), new Date(2026, 12, 31));
Guid guidUmowy = Guid.Empty;
InTransaction(() =>
{
var u = Session.AddRow(new Umowa(pracownik));
u.Element = DefUmowy(DefinicjaElementu.UmowaZlecenie);
u.Data = okres.From;
u.Okres = okres;
u.Tytul = "Umowa zlecenie - zgłoszenie ZUS";
u.RodzajRozliczenia = RodzajeRozliczeniaUmowy.KwotaDoWypłaty;
u.TypWartosci = TypWartosciUmowy.Brutto;
u.Wydzial = Kadry.Wydzialy.Firma;
u.Last.Wartosc = new Currency(4000m);
guidUmowy = u.Guid;
});
SaveDispose();
var umowa = Get(guidUmowy);
// Worker zgłoszeniowy na typie Umowa — operuje na zaznaczonych umowach.
// Uwaga: Umowy oraz Pars są write-only (set-only) — przekazujemy je przez inicjalizator/setter.
var worker = new Soneta.Deklaracje.ZUS.ZarejestrujUmowyWorker { Umowy = new[] { umowa } };
// Parametry zgłoszenia: ParamsZ(Context) — bazowe Okres/DataDokumentu/DataWypełnienia ustawiane
// na wspólnym kontrakcie ZarejestrujBaseWorker (ParamsZ przekazujemy jako Pars do Rejestracji).
var pars = new Soneta.Deklaracje.ZUS.ZarejestrujBaseWorker.ParamsZ(Context);
pars.ZarejestrujRodzinę = false;
var rejestracja = new Soneta.Deklaracje.ZUS.ZarejestrujUmowyWorker.Rejestracja { Pars = pars };
// Generowanie (ZUA/ZZA wg schematu ubezpieczeń) — wymaga kontekstu deklaracji/KEDU:
rejestracja.ZarejestrujUmowy();
SaveDispose();
}
}