using System; using System.Collections; using System.Linq; using AwesomeAssertions; using NUnit.Framework; using Soneta.Kasa; // EksportPrzelewowWorker, EksportPrzelewowParams, PrzelewBase, PaczkaPrzelewow, RachunekBankowyFirmy, RozrachunekIdx, ... using Soneta.Place; // ListaPlac (+ ListaPlac.PrzygotujPrzelewyWorker) using Prac = Soneta.Kadry.Pracownik; namespace Soneta.Skills.Test.KadryPlace.Pracownik; /// /// Rozdział I (część rozliczeniowa) — „Przelewy wynagrodzeń, eksport do banku, rozliczenia/faktury” /// (receptury I4, I5, I6). /// /// Testy są wykonywalną dokumentacją publicznego kontraktu mechanizmu „z wypłaty do przelewu” /// i rozliczeń pracownika. Operujemy wyłącznie na publicznym kontrakcie platformy Soneta /// (jak dodatek programisty zewnętrznego), na bazie Demo (GoldStandard) z automatycznym rollbackiem. /// /// /// I4 — przygotowanie przelewów wynagrodzeń workerem /// Soneta.Place.ListaPlac.PrzygotujPrzelewyWorker (akcja PrzygotujPrzelewy()). /// Testowalny jest kontrakt (istnienie workera i jego Params z polami /// Data/Paczka/ZRachunku) oraz odczyt kolekcji rozliczeniowych pracownika /// (Przelewy, DokumentyPreliminarza, Rozrachunki). Samo /// worker.PrzygotujPrzelewy() wymaga skonfigurowanego modułu Kasa (definicja paczki, rachunek /// firmy, rachunek pracownika), czego baza Demo nie gwarantuje → [Ignore]. /// I5 — eksport przelewów do pliku bankowego workerem /// Soneta.Kasa.EksportPrzelewowWorker (akcja Eksport()) sterowanym /// Soneta.Kasa.EksportPrzelewowParams. Testowalne jest istnienie publicznego API /// (konstrukcja workera i parametrów, pole FileName). Wywołanie Eksport() to operacja /// plikowa/sieciowa → [Ignore]. /// I6 — rozliczenia/faktura: odczyt kolekcji rozrachunkowych pracownika /// (Rozrachunki, DokumentyRozliczeniowe, DokumentyPreliminarza) — asercja, że są /// dostępne, iterowalne i zwracają typy zgodne z kontraktem. Wystawienie faktury (zbiorczej) z zapłaty /// to domena handlowa (DokumentHandlowy), poza kontraktem pracownika → [Ignore]. /// /// [TestFixture] public class RozdzialIrest_PrzelewyTest : PracownikTestBase { // =================================================================================== // I4 — Przygotowanie przelewów wynagrodzeń (kontrakt workera + odczyt kolekcji) // =================================================================================== [Test] [Description("I4 (kontrakt): worker przygotowania przelewów z listy płac istnieje w publicznym API — " + "Soneta.Place.ListaPlac.PrzygotujPrzelewyWorker z zagnieżdżonym typem Params. " + "Asercja przez refleksję publicznego kontraktu: typ workera i Params istnieją, Params ma " + "pola Data/Paczka/DefinicjaPaczki/ZRachunku, a worker ma metodę PrzygotujPrzelewy(). " + "Faktyczne wywołanie PrzygotujPrzelewy() jest [Ignore] (osobny test) — wymaga konfiguracji Kasa.")] public void I4_PrzygotujPrzelewy_KontraktWorkera() { // Worker płacowy jest typem zagnieżdżonym w ListaPlac (assembly Soneta.KadryPlace, namespace Soneta.Place). Type workerType = typeof(ListaPlac.PrzygotujPrzelewyWorker); workerType.Should().NotBeNull("worker przygotowania przelewów istnieje w publicznym kontrakcie"); // Typ parametrów workera (zagnieżdżony Params). Type paramsType = workerType.GetNestedType("Params"); paramsType.Should().NotBeNull("PrzygotujPrzelewyWorker udostępnia publiczny typ Params"); // Kluczowe pola/właściwości parametrów wg dokumentacji I4. var skladowe = paramsType.GetMembers() .Select(m => m.Name) .ToList(); skladowe.Should().Contain("Data", "Params.Data — data dokumentów przelewu"); skladowe.Should().Contain("Paczka", "Params.Paczka — istniejąca paczka przelewów"); skladowe.Should().Contain("ZRachunku", "Params.ZRachunku — rachunek firmy obciążany przelewami"); // Akcja workera: PrzygotujPrzelewy(). workerType.GetMethod("PrzygotujPrzelewy") .Should().NotBeNull("worker udostępnia akcję PrzygotujPrzelewy()"); // Dokument przelewu, który powstaje w wyniku akcji, to Soneta.Kasa.PrzelewBase (tabela Przelewy). typeof(PrzelewBase).Should().NotBeNull("dokument przelewu to Soneta.Kasa.PrzelewBase"); } [Test] [Description("I4 (odczyt): kolekcje rozliczeniowe pracownika są dostępne i iterowalne — " + "Pracownik.Przelewy (PrzelewBase), Pracownik.DokumentyPreliminarza (PreliminarzDokument), " + "Pracownik.Rozrachunki (RozrachunekIdx). Asercja: iteracja nie rzuca, a elementy (jeśli są) " + "mają typy zgodne z kontraktem. Bez wywołania PrzygotujPrzelewy — sam odczyt stanu.")] public void I4_KolekcjeRozliczeniowePracownika_OdczytTypyZgodne() { var pracownik = Pracownik(Pracownik_.Andrzejewski); pracownik.Should().NotBeNull(); // Przelewy — odczyt nie rzuca; elementy (jeśli są) to PrzelewBase. Action czytajPrzelewy = () => IterujISprawdzTyp(pracownik.Przelewy); czytajPrzelewy.Should().NotThrow("odczyt kolekcji Pracownik.Przelewy jest bezpieczny"); // Dokumenty preliminarza — elementy to PreliminarzDokument. Action czytajPreliminarz = () => IterujISprawdzTyp(pracownik.DokumentyPreliminarza); czytajPreliminarz.Should().NotThrow("odczyt kolekcji Pracownik.DokumentyPreliminarza jest bezpieczny"); // Rozrachunki — elementy to RozrachunekIdx. Action czytajRozrachunki = () => IterujISprawdzTyp(pracownik.Rozrachunki); czytajRozrachunki.Should().NotThrow("odczyt kolekcji Pracownik.Rozrachunki jest bezpieczny"); } [Test] [Ignore("I4: faktyczne wywołanie ListaPlac.PrzygotujPrzelewyWorker.PrzygotujPrzelewy() wymaga " + "skonfigurowanego modułu Kasa (definicja paczki przelewów DefinicjaPaczkiPrzelewu, rachunek firmy " + "RachunekBankowyFirmy oraz rachunek odbiorcy Pracownik.Rachunki). Baza Demo nie gwarantuje tej " + "konfiguracji, więc generowanie dokumentów PrzelewBase jest niepewne. Test I4_PrzygotujPrzelewy_KontraktWorkera " + "pokrywa publiczny kontrakt; samo przygotowanie przelewów dokumentujemy bez uruchamiania.")] [Description("I4 (wykonanie — pominięte): naliczenie wypłaty etatowej (jak H1/I1b) → ListaPlac z Wyplata.ListaPlac → " + "new ListaPlac.PrzygotujPrzelewyWorker { Pars = new Params { Data = Date.Today, ... } }.PrzygotujPrzelewy() → " + "session.Save(). Powstają dokumenty Soneta.Kasa.PrzelewBase w paczce PaczkaPrzelewow.")] public void I4_PrzygotujPrzelewy_Wykonanie() { // Pominięte — patrz powód w [Ignore]. Operacja zapisująca zależna od konfiguracji modułu Kasa. } // =================================================================================== // I5 — Eksport przelewów do pliku bankowego (istnienie API; eksport pliku → Ignore) // =================================================================================== [Test] [Description("I5 (kontrakt API): eksport przelewów to worker Soneta.Kasa.EksportPrzelewowWorker " + "sterowany Soneta.Kasa.EksportPrzelewowParams. UWAGA: EksportPrzelewowParams NIE ma " + "konstruktora bezparametrowego — wymaga (Context, RachunekBankowyFirmy, PrzelewBase[]), a sam " + "konstruktor RZUCA ApplicationException, gdy nie wskazano rachunku firmy (walidacja w ctorze). " + "Dlatego kontrakt weryfikujemy REFLEKSJĄ (bez instancjonowania): istnienie typów, sygnatura " + "konstruktora parametrów, publiczne pole FileName, worker + property Params i metoda Eksport().")] public void I5_EksportPrzelewow_KontraktApi() { // Typ parametrów eksportu istnieje w publicznym kontrakcie. Type paramsType = typeof(EksportPrzelewowParams); paramsType.Should().NotBeNull("EksportPrzelewowParams istnieje w publicznym kontrakcie"); // Konstruktor parametrów wymaga (Context, RachunekBankowyFirmy, PrzelewBase[]) — sygnatura wg kontraktu. // (NIE wołamy go: ctor waliduje rachunek i rzuca ApplicationException przy braku konfiguracji.) var ctor = paramsType.GetConstructor(new[] { typeof(Soneta.Business.Context), typeof(RachunekBankowyFirmy), typeof(PrzelewBase[]), }); ctor.Should().NotBeNull( "EksportPrzelewowParams wymaga konstruktora (Context, RachunekBankowyFirmy, PrzelewBase[])"); // Publiczne pole ścieżki pliku wyjściowego. paramsType.GetProperty("FileName") .Should().NotBeNull("Params.FileName — ścieżka pliku wyjściowego (operacja na dysku)"); // Worker eksportu i jego property Params (sterowanie parametrami). Type workerType = typeof(EksportPrzelewowWorker); workerType.Should().NotBeNull("EksportPrzelewowWorker istnieje w publicznym kontrakcie"); workerType.GetProperty("Params") .Should().NotBeNull("worker przyjmuje parametry przez właściwość Params"); // Akcja eksportu istnieje w kontrakcie (ale jej NIE wołamy — patrz I5_EksportPrzelewow_Eksport). workerType.GetMethod("Eksport") .Should().NotBeNull("worker udostępnia akcję Eksport() — w teście jednostkowym nie wywoływaną"); } [Test] [Ignore("I5: EksportPrzelewowWorker.Eksport() zapisuje fizyczny plik bankowy na dysk (wg Params.FileName) " + "i zależy od formatu/sterownika eksportu danego banku; wysyłka online to dodatkowo operacja sieciowa. " + "To wejście/wyjście do systemu zewnętrznego — poza zakresem testu jednostkowego. Kontrakt API " + "pokrywa test I5_EksportPrzelewow_KontraktApi (bez wywołania Eksport()).")] [Description("I5 (wykonanie — pominięte): worker.Eksport() — zapis pliku przelewów wg FileName; po eksporcie " + "PrzelewBase.Exported = true blokuje dalszą edycję.")] public void I5_EksportPrzelewow_Eksport() { // Pominięte — patrz powód w [Ignore]. Operacja plikowa/sieciowa. } // =================================================================================== // I6 — Rozliczenia / faktura (odczyt rozrachunków; wystawienie faktury → Ignore) // =================================================================================== [Test] [Description("I6 (odczyt): kolekcje rozliczeniowe pracownika są dostępne i iterowalne, a elementy mają " + "typy zgodne z kontraktem — Pracownik.Rozrachunki (RozrachunekIdx), " + "Pracownik.DokumentyRozliczeniowe (DokRozliczBase), Pracownik.DokumentyPreliminarza " + "(PreliminarzDokument). Asercja: iteracja nie rzuca; bez operacji zapisujących.")] public void I6_Rozliczenia_OdczytStanu() { var pracownik = Pracownik(Pracownik_.Bednarek); pracownik.Should().NotBeNull(); // Rozrachunki — indeksy rozrachunkowe podmiotu (RozrachunekIdx). Action czytajRozrachunki = () => IterujISprawdzTyp(pracownik.Rozrachunki); czytajRozrachunki.Should().NotThrow("odczyt kolekcji Pracownik.Rozrachunki jest bezpieczny"); // Dokumenty rozliczeniowe — DokRozliczBase. Action czytajRozliczeniowe = () => IterujISprawdzTyp(pracownik.DokumentyRozliczeniowe); czytajRozliczeniowe.Should().NotThrow("odczyt kolekcji Pracownik.DokumentyRozliczeniowe jest bezpieczny"); // Dokumenty preliminarza — PreliminarzDokument. Action czytajPreliminarz = () => IterujISprawdzTyp(pracownik.DokumentyPreliminarza); czytajPreliminarz.Should().NotThrow("odczyt kolekcji Pracownik.DokumentyPreliminarza jest bezpieczny"); } [Test] [Ignore("I6: „Wystaw fakturę (zbiorczą) z zapłaty” NIE istnieje w publicznym kontrakcie pracownika/płac — " + "faktura to dokument handlowy (Soneta.Handel.DokumentHandlowy). Powiązanie zapłaty z fakturą realizują " + "rozrachunki/rozliczenia (moduł Kasa), a operacje zapisujące (np. RozliczWgPrzelewowWyplataWorker) wymagają " + "skonfigurowanego modułu Kasa/Handel, którego baza Demo nie gwarantuje. Wystawianie faktur należy do testów " + "domeny handlowej (dokument-handlowy.md). Odczyt rozrachunków pokrywa test I6_Rozliczenia_OdczytStanu.")] [Description("I6 (wykonanie — pominięte): wystawienie faktury zbiorczej z zapłat/rozliczeń (domena handlowa) " + "oraz rozliczanie zapisujące przez workery rozliczeniowe Kasa.")] public void I6_WystawienieFaktury_Rozliczenie() { // Pominięte — patrz powód w [Ignore]. Domena handlowa + konfiguracja Kasa/Handel. } // =================================================================================== // Pomocniki lokalne // =================================================================================== /// /// Iteruje kolekcję (np. SubTable<T> z kartoteki pracownika) i sprawdza, że każdy /// element jest przypisywalny do oczekiwanego typu kontraktu. Sama iteracja po kolekcji /// rozliczeniowej pracownika jest bezpieczna (zakres = jeden podmiot), więc nie skanujemy całej /// tabeli operacyjnej (safe-code §6.3). Pusta kolekcja jest poprawna (brak danych w Demo). /// private static void IterujISprawdzTyp(IEnumerable kolekcja) { kolekcja.Should().NotBeNull("kolekcja rozliczeniowa pracownika jest dostępna w kontrakcie"); foreach (var element in kolekcja) element.Should().BeAssignableTo($"elementy kolekcji są typu {typeof(T).Name} (zgodnie z kontraktem)"); } }