Files
soneta-erp-skills/Soneta.Skills.Test/KadryPlace/Pracownik/RozdzialJ_DeklaracjeTest.cs
T
2026-06-06 22:33:15 +02:00

284 lines
17 KiB
C#
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.
using System;
using System.Linq;
using AwesomeAssertions;
using NUnit.Framework;
using Soneta.Business;
using Soneta.Deklaracje;
using Soneta.Kadry;
using Soneta.Place;
using Soneta.Types;
using Prac = Soneta.Kadry.Pracownik;
namespace Soneta.Skills.Test.KadryPlace.Pracownik;
/// <summary>
/// Rozdział J — „Deklaracje (ZUS, PIT, PFRON, PPK)" (receptury J1J6).
/// <para>
/// Testy są <b>wykonywalną dokumentacją</b> publicznego kontraktu modułu Deklaracje
/// (<c>Soneta.Deklaracje.DeklaracjeModule</c>, dostęp przez <c>Session.GetDeklaracje()</c>).
/// Wszystkie deklaracje to wiersze tabeli <c>Deklaracje</c>, dziedziczące po abstrakcyjnej
/// <c>Soneta.Deklaracje.Deklaracja</c>; konkretne typy żyją w podprzestrzeniach
/// <c>Soneta.Deklaracje.{ZUS,PIT,PFRON,PPK}.*</c>.
/// </para>
/// <para>
/// <b>Rozróżnienie kluczowe.</b> Naliczenie/utworzenie większości deklaracji (J1J5) to operacja
/// lokalna (zapis wiersza), ale wymaga <c>Context</c> i — dla ZUS — obiektu <c>KEDU</c> (kontener
/// dokumentów ZUS), którego nie da się sensownie zbudować bez środowiska modułu Deklaracje.
/// E-wysyłka (KEDU/PUE/SODiR/MF) jest sieciowa/plikowa. Dlatego testy J1J5 dokumentują
/// <b>KONTRAKT</b> typów/workerów kompilowalnie (przez odwołania do typów <c>typeof(...)</c>,
/// ctory, metody) i są oznaczone <c>[Ignore]</c> z powodem. Realnie wykonujemy J6 (bilanse otwarcia
/// PIT — czyste API biznesowe na pracowniku) oraz próbę naliczenia PIT-11.
/// </para>
/// <para>
/// Operujemy wyłącznie na <b>publicznym kontrakcie</b> platformy, na bazie Demo (GoldStandard),
/// z automatycznym rollbackiem po teście.
/// </para>
/// </summary>
[TestFixture]
public class RozdzialJ_DeklaracjeTest : PracownikTestBase
{
/// <summary>Skrót do modułu Deklaracje bieżącej sesji operacyjnej.</summary>
private DeklaracjeModule Deklaracje => Session.GetDeklaracje();
// ============================== J1 — Zgłoszenia ZUS (ZUA/ZZA, ZCNA, ZWUA) ==============================
[Test]
[Description("J1: zgłoszenia ZUS to wiersze deklaracji w Soneta.Deklaracje.ZUS — ZUA (społeczne+zdrowotne), " +
"ZZA (zdrowotne), ZCNA (rodzina), ZWUA (wyrejestrowanie). Konkretne typy mają ctor " +
"(Pracownik, KEDU): new ZUA(pracownik, kedu). Workerem zbiorczym jest " +
"ZarejestrujPracownikówWorker (zagnieżdżone .Rejestracja/.Rodzina/.Wyrejestrowanie/.ZgloszenieUmow), " +
"Params budowane z Context (ctor (Context)) + pole Kedu. Tu dokumentujemy KONTRAKT typów; " +
"samo utworzenie wymaga Context + KEDU.")]
[Ignore("wymaga Context/KEDU / e-wysyłka sieciowa — dokumentowany kontrakt typów ZUS")]
public void J1_ZgloszeniaZUS_ZUA_ZZA_ZCNA_ZWUA_Kontrakt()
{
// Kontrakt typów zgłoszeniowych ZUS — odwołania kompilowalne (zweryfikowane z DLL).
typeof(Soneta.Deklaracje.ZUS.ZUA).Should().NotBeNull("ZUA — zgłoszenie społeczne+zdrowotne");
typeof(Soneta.Deklaracje.ZUS.ZZA).Should().NotBeNull("ZZA — zgłoszenie tylko zdrowotne");
typeof(Soneta.Deklaracje.ZUS.ZCNA).Should().NotBeNull("ZCNA — zgłoszenie członków rodziny");
typeof(Soneta.Deklaracje.ZUS.ZWUA).Should().NotBeNull("ZWUA — wyrejestrowanie");
// Worker zbiorczy + jego klasy zagnieżdżone (akcje menu „Deklaracje ZUS/Przygotuj …").
typeof(Soneta.Deklaracje.ZUS.ZarejestrujPracownikówWorker.Rejestracja).Should().NotBeNull();
typeof(Soneta.Deklaracje.ZUS.ZarejestrujPracownikówWorker.Rodzina).Should().NotBeNull();
typeof(Soneta.Deklaracje.ZUS.ZarejestrujPracownikówWorker.Wyrejestrowanie).Should().NotBeNull();
// Params zgłoszeniowe mają ctor (Context); KEDU jest wymaganym kontenerem docelowym.
typeof(Soneta.Deklaracje.ZUS.ZarejestrujBaseWorker.ParamsKor)
.GetConstructor(new[] { typeof(Context) })
.Should().NotBeNull("ParamsKor budujemy z Context");
typeof(Soneta.Deklaracje.ZUS.KEDU)
.GetConstructor(new[] { typeof(Session) })
.Should().NotBeNull("KEDU ma ctor (Session), ale realne złożenie wymaga modułu Deklaracje");
}
// ============================== J2 — Deklaracje rozliczeniowe ZUS (DRA, RIA, IMIR/RMUA) ==============================
[Test]
[Description("J2: rozliczeniowe ZUS — DRA (deklaracja rozliczeniowa, ctor (KEDU)), RIA (raport po ustaniu, " +
"ctor (Pracownik, KEDU)), RMUA (informacja miesięczna dla ubezpieczonego = IMIR, ctor " +
"(Pracownik, RMUA.TypOkresuDeklaracji)). Naliczanie seryjne: NaliczanieSeryjneRIAWorker / " +
"NaliczanieSeryjneRMUAWorker (ctor bezparametrowy + Pracownicy/Pars + metoda NaliczRMUA(Context)). " +
"Pojedynczą deklarację przelicza DeklaracjaWorker.Przelicz() (DataType Deklaracja). " +
"KEDU + Context wymagane — dokumentujemy KONTRAKT.")]
[Ignore("wymaga Context/KEDU / e-wysyłka sieciowa — dokumentowany kontrakt rozliczeń ZUS")]
public void J2_RozliczeniaZUS_DRA_RIA_IMIR_Kontrakt()
{
// DRA wiąże się z KEDU (ctor (KEDU)), RIA z pracownikiem i KEDU.
typeof(Soneta.Deklaracje.ZUS.DRA).GetConstructor(new[] { typeof(Soneta.Deklaracje.ZUS.KEDU) })
.Should().NotBeNull("DRA(KEDU)");
typeof(Soneta.Deklaracje.ZUS.RIA)
.GetConstructor(new[] { typeof(Prac), typeof(Soneta.Deklaracje.ZUS.KEDU) })
.Should().NotBeNull("RIA(Pracownik, KEDU)");
// IMIR w CLR nazywa się RMUA (ctor (Pracownik, RMUA.TypOkresuDeklaracji)).
typeof(Soneta.Deklaracje.ZUS.RMUA).Should().NotBeNull("RMUA = informacja miesięczna (IMIR)");
typeof(Soneta.Deklaracje.ZUS.RMUA.TypOkresuDeklaracji).IsEnum
.Should().BeTrue("typ okresu deklaracji RMUA jest enumem");
// Naliczanie seryjne RIA/RMUA — ctor bezparametrowy + Pracownicy/Pars (Context w props).
typeof(Soneta.Deklaracje.ZUS.NaliczanieSeryjneRIAWorker).GetConstructor(Type.EmptyTypes)
.Should().NotBeNull();
typeof(Soneta.Deklaracje.ZUS.NaliczanieSeryjneRMUAWorker).GetMethod("NaliczRMUA")
.Should().NotBeNull("NaliczRMUA(Context) — metoda akcji naliczania IMIR");
// Przeliczenie istniejącego wiersza dowolnej deklaracji.
typeof(DeklaracjaWorker).GetMethod("Przelicz").Should().NotBeNull("DeklaracjaWorker.Przelicz()");
}
// ============================== J3 — Deklaracje PIT (PIT-11, 4R, 8AR, R, IFT) ==============================
[Test]
[Description("J3: imienne PIT (PIT-11, PIT-R, IFT-1/IFT-1R, PIT-8C) nalicza seryjnie zagnieżdżony " +
"Soneta.Deklaracje.PIT.NaliczanieSeryjne.* (PIT_11Worker ma ctor (Session); Params ctor (Context)). " +
"PIT-4R/PIT-8AR (PIT4/PIT8A) są zbiorcze na poziomie podmiotu/US (ctory nonpublic — tworzone " +
"workerami zbiorczymi). Tu dokumentujemy KONTRAKT typów i workerów. Realne naliczenie PIT-11 " +
"próbujemy w J3b.")]
[Ignore("wymaga Context / dane źródłowe (wypłaty + BO PIT) — dokumentowany kontrakt PIT")]
public void J3_DeklaracjePIT_Kontrakt()
{
// Typy deklaracji PIT (wiersze tabeli Deklaracje).
typeof(Soneta.Deklaracje.PIT.PIT11).Should().NotBeNull("PIT-11");
typeof(Soneta.Deklaracje.PIT.PIT4).Should().NotBeNull("PIT-4R (zaliczki)");
typeof(Soneta.Deklaracje.PIT.PIT8A).Should().NotBeNull("PIT-8AR (zryczałtowany)");
typeof(Soneta.Deklaracje.PIT.PITR).Should().NotBeNull("PIT-R");
typeof(Soneta.Deklaracje.PIT.IFT1).Should().NotBeNull("IFT-1/IFT-1R");
// Workery naliczania seryjnego PIT (zagnieżdżone w NaliczanieSeryjne).
typeof(Soneta.Deklaracje.PIT.NaliczanieSeryjne.PIT_11Worker)
.GetConstructor(new[] { typeof(Session) })
.Should().NotBeNull("PIT_11Worker(Session)");
typeof(Soneta.Deklaracje.PIT.NaliczanieSeryjne.PIT_RWorker).Should().NotBeNull();
typeof(Soneta.Deklaracje.PIT.NaliczanieSeryjne.IFT_1Worker).Should().NotBeNull();
typeof(Soneta.Deklaracje.PIT.NaliczanieSeryjne.IFT_1RWorker).Should().NotBeNull();
typeof(Soneta.Deklaracje.PIT.NaliczanieSeryjne.PIT_8CWorker).Should().NotBeNull();
// Params PIT mają ctor (Context).
typeof(Soneta.Deklaracje.PIT.NaliczanieSeryjne.PIT_11Worker.Params)
.GetConstructor(new[] { typeof(Context) })
.Should().NotBeNull("PIT_11Worker.Params(Context)");
}
[Test]
[Description("J3b: próba realnego naliczenia PIT-11 dla pracownika Demo workerem " +
"NaliczanieSeryjne.PIT_11Worker(Session) { Pracownicy = [...] }, ustawiając Pars.Okres (rok) " +
"i Pars.Data, a następnie wywołując Nalicz_PIT_11(). Worker wymaga środowiska Context/danych " +
"źródłowych — w razie wyjątku oznaczamy [Ignore].")]
[Ignore("PIT_11Worker wymaga Context/KEDU oraz danych źródłowych (naliczone wypłaty + BO PIT); " +
"naliczenie w izolacji testu rzuca — dokumentowany kontrakt wywołania")]
public void J3b_NaliczeniePIT11_ProbaRealna()
{
var pracownik = Pracownik(Pracownik_.Andrzejewski);
pracownik.Should().NotBeNull();
var worker = new Soneta.Deklaracje.PIT.NaliczanieSeryjne.PIT_11Worker(Session)
{
Pracownicy = new[] { pracownik },
};
worker.Pars.Okres = FromTo.Year(2025); // rok podatkowy
worker.Pars.Data = Date.Today;
worker.Nalicz_PIT_11(); // tworzy wiersze PIT11 w tabeli Deklaracje
SaveDispose();
}
// ============================== J4 — Deklaracje PFRON (Wn-D, INF-2, DEK-R, INF-D-P) ==============================
[Test]
[Description("J4: PFRON to wiersze deklaracji w Soneta.Deklaracje.PFRON — WN_D (Wn-D), INF_2 (informacja " +
"roczna), DEK_R (deklaracja roczna wpłat), INF_D_P (załącznik o pracowniku niepełnosprawnym). " +
"PFRON nie ma seryjnego naliczania na Pracownicy — deklarację tworzy się w module Deklaracje, " +
"a przelicza DeklaracjaWorker.Przelicz() (DataType Deklaracja). Dane źródłowe pochodzą z " +
"PracHistoria.PFRON (A13). Tworzenie/edycja wymaga Context — dokumentujemy KONTRAKT.")]
[Ignore("wymaga Context / e-wysyłka SODiR — dokumentowany kontrakt typów PFRON")]
public void J4_DeklaracjePFRON_Kontrakt()
{
typeof(Soneta.Deklaracje.PFRON.WN_D).Should().NotBeNull("Wn-D — wniosek o dofinansowanie");
typeof(Soneta.Deklaracje.PFRON.INF_2).Should().NotBeNull("INF-2 — informacja roczna");
typeof(Soneta.Deklaracje.PFRON.DEK_R).Should().NotBeNull("DEK-R — deklaracja roczna wpłat");
typeof(Soneta.Deklaracje.PFRON.INF_D_P).Should().NotBeNull("INF-D-P — załącznik o pracowniku");
// Wszystkie PFRON dziedziczą po Deklaracja, więc przelicza je wspólny DeklaracjaWorker.
typeof(Soneta.Deklaracje.PFRON.WN_D).IsSubclassOf(typeof(Deklaracja))
.Should().BeTrue("PFRON to wiersze tabeli Deklaracje");
typeof(DeklaracjaWorker).GetMethod("Przelicz").Should().NotBeNull();
}
// ============================== J5 — Operacje PPK ==============================
[Test]
[Description("J5: dokumenty PPK to wiersze deklaracji w Soneta.Deklaracje.PPK (RejestracjaUczestnikaPPK, " +
"DeklaracjaUczestnikaPPK, ZakończenieZatrudnieniaUczestnikaPPK, RozliczenieSkładekPPK, …). " +
"Operacje zbiorcze na Pracownicy realizuje DeklaracjePPKPracownikówWorker (zagnieżdżone " +
".Rejestracja/.Rezygnacja/.Wznowienie/.ZakończenieZatrudnienia/.ZmianaDanychIdentyfikacyjnych); " +
"wspólny Params = DeklaracjePPKBaseWorker.Params (ctor (Context), pole DokumentPPK). " +
"Kwalifikacja/auto-zapis to workery na pracowniku (PPKWorker/AutoZapisPPKWorker, ctor (Context)). " +
"Dokumentujemy KONTRAKT — operacje wymagają Context i zwykle DokumentyPracodawcyPPK.")]
[Ignore("wymaga Context / DokumentyPracodawcyPPK — dokumentowany kontrakt operacji PPK")]
public void J5_OperacjePPK_Kontrakt()
{
// Typy dokumentów PPK.
typeof(Soneta.Deklaracje.PPK.RejestracjaUczestnikaPPK).Should().NotBeNull();
// Workery zbiorcze operacji PPK (zagnieżdżone w DeklaracjePPKPracownikówWorker).
typeof(Soneta.Deklaracje.PPK.DeklaracjePPKPracownikówWorker.Rejestracja).Should().NotBeNull();
typeof(Soneta.Deklaracje.PPK.DeklaracjePPKPracownikówWorker.Rezygnacja).Should().NotBeNull();
typeof(Soneta.Deklaracje.PPK.DeklaracjePPKPracownikówWorker.Wznowienie).Should().NotBeNull();
typeof(Soneta.Deklaracje.PPK.DeklaracjePPKPracownikówWorker.ZakończenieZatrudnienia).Should().NotBeNull();
typeof(Soneta.Deklaracje.PPK.DeklaracjePPKPracownikówWorker.ZmianaDanychIdentyfikacyjnych).Should().NotBeNull();
// Wspólny Params ma ctor (Context).
typeof(Soneta.Deklaracje.PPK.DeklaracjePPKBaseWorker.Params)
.GetConstructor(new[] { typeof(Context) })
.Should().NotBeNull("DeklaracjePPKBaseWorker.Params(Context)");
}
// ============================== J6 — Bilanse otwarcia PIT (REALNIE TESTOWALNE) ==============================
[Test]
[Description("J6: bilans otwarcia PIT to kolekcja na pracowniku (pracownik.BilansyOtwarciaPIT, " +
"SubTable<Soneta.Place.BilansOtwarciaPIT>). Tworzymy czystym API biznesowym (BEZ Context/KEDU): " +
"Session.AddRow(new BilansOtwarciaPIT_29(pracownik)) w trybie edycji; ustawiamy Data oraz kwoty " +
"(PrzychodUlgaEtat, Spoleczne). UWAGA: bazowy BilansOtwarciaPIT jest ABSTRAKCYJNY — instancjonujemy " +
"konkretną wersję BilansOtwarciaPIT_29 (Wersja=PIT11_29) lub BilansOtwarciaPIT_11 (PIT11_11), " +
"ctor (Pracownik); brak ctora bezparametrowego, Pracownik read-only. Odczyt przez " +
"pracownik.BilansyOtwarciaPIT.")]
public void J6_BilansOtwarciaPIT_TworzenieIOdczyt()
{
var pracownik = Pracownik(Pracownik_.Andrzejewski);
pracownik.Should().NotBeNull();
// Stan początkowy kolekcji bilansów otwarcia PIT.
int przed = pracownik.BilansyOtwarciaPIT.Cast<BilansOtwarciaPIT>().Count();
var data = new Date(2026, 1, 1);
Guid guidBO = Guid.Empty;
// Tworzenie danych operacyjnych MUSI być w trybie edycji (InTransaction),
// inaczej AddRow rzuca CannotEditException.
InTransaction(() =>
{
// Bazowy BilansOtwarciaPIT jest abstrakcyjny — tworzymy konkretną wersję (_29 => PIT11_29).
BilansOtwarciaPIT bo = Session.AddRow(new BilansOtwarciaPIT_29(pracownik));
bo.Data = data;
bo.PrzychodUlgaEtat = 12000m;
bo.Spoleczne = 1645.20m;
guidBO = bo.Guid;
});
SaveDispose(); // utrwalenie (rollback po teście i tak wycofa)
// Odczyt: bilans jest dopięty do pracownika i ma ustawione wartości.
var boWczytany = Get<BilansOtwarciaPIT>(guidBO);
boWczytany.Should().NotBeNull("bilans otwarcia PIT został zapisany");
boWczytany.Pracownik.Guid.Should().Be(pracownik.Guid, "bilans jest powiązany z pracownikiem");
boWczytany.Data.Should().Be(data);
boWczytany.PrzychodUlgaEtat.Should().Be(12000m);
boWczytany.Spoleczne.Should().Be(1645.20m);
boWczytany.Wersja.Should().Be(WersjaBilansuOtwarciaPIT.PIT11_29, "wersja ustawiana w ctor");
// Odczyt przez kolekcję pracownika — bilans jest widoczny.
var pracownik2 = Pracownik(Pracownik_.Andrzejewski);
var bilanse = pracownik2.BilansyOtwarciaPIT.Cast<BilansOtwarciaPIT>().ToList();
bilanse.Should().HaveCount(przed + 1, "doszedł jeden bilans otwarcia PIT");
bilanse.Should().Contain(b => b.Guid == guidBO);
}
[Test]
[Description("J6b: pozostałe kolekcje wdrożeniowe ERP-7 na pracowniku — pracownik.WynagrodzeniaERP7 " +
"(SubTable<Soneta.Kalend.WynagrodzenieERP7>) i pracownik.NieobecnosciERP7 " +
"(SubTable<Soneta.Kalend.NieobecnoscERP7>). Dokumentujemy KONTRAKT (kolekcje istnieją i są " +
"iterowalne czystym API, bez Context); sam druk Z-3/ERP-7 to generowanie w module Deklaracje.")]
public void J6b_KolekcjeERP7_Odczyt()
{
var pracownik = Pracownik(Pracownik_.Andrzejewski);
pracownik.Should().NotBeNull();
// Kolekcje istnieją i są iterowalne (na Demo zwykle puste — sprawdzamy sam kontrakt).
System.Action odczytWynagrodzen = () => pracownik.WynagrodzeniaERP7.Cast<object>().ToList();
System.Action odczytNieobecnosci = () => pracownik.NieobecnosciERP7.Cast<object>().ToList();
odczytWynagrodzen.Should().NotThrow("kolekcja WynagrodzeniaERP7 jest dostępna czystym API");
odczytNieobecnosci.Should().NotThrow("kolekcja NieobecnosciERP7 jest dostępna czystym API");
}
}