using AwesomeAssertions;
using NUnit.Framework;
using Soneta.Handel;
using Soneta.Magazyny;
namespace Soneta.Skills.Test.Handel.DokumentyHandlowe;
///
/// Rozdział 3 — Stany dokumentu i cykl życia (W12–W16).
///
/// Stanem dokumentu steruje jedno zapisywalne pole dok.Stan
/// (: Bufor=0, Zatwierdzony=1, Zablokowany=2, Anulowany=3).
/// Do asercji używamy skrótów kalkulowanych dok.Bufor/dok.Zatwierdzony/dok.Anulowany,
/// a nie porównywania enuma.
///
///
/// W bazie Demo działa StanUjemnyVerifier (blokada stanu ujemnego): zatwierdzenie rozchodu
/// wymaga wcześniej zapisanego przyjęcia tego towaru. Dlatego do prostych testów cyklu życia
/// używamy przychodu (PW), który niczego nie blokuje. Magazyn księguje się dopiero po
/// Session.Save(), nie po samym Commit().
///
/// Cała klasa operuje wyłącznie na publicznym kontrakcie platformy (tak jak dodatek zewnętrzny).
///
[TestFixture]
public class Rozdzial03_CyklZyciaTest : DokumentHandlowyTestBase
{
// === Pomocnik lokalny: zatwierdzony przychód (PW) z jedną pozycją, zapisany trwale ===
///
/// Tworzy przyjęcie wewnętrzne (PW) z pozycją towaru BIKINI, zatwierdza je i zapisuje.
/// PW to przychód — nie podlega blokadzie stanu ujemnego, więc nadaje się do testów cyklu życia.
/// Zwraca Guid zapisanego dokumentu (sesja zostaje zamknięta przez ).
///
private System.Guid UtworzZatwierdzonyPwIZapisz(double ilosc = 10, double cena = 5)
{
// 1. Dokument przychodowy + pozycja w jednej transakcji.
var dok = UtworzDokument(Definicje.PrzyjecieWewnetrzne, magazyn: Magazyn(Magazyn_.Firma));
InTransaction(() => DodajPozycje(dok, Towar(Towar_.Bikini), ilosc, cena));
// 2. Zatwierdzenie: Bufor -> Zatwierdzony (osobna transakcja).
InTransaction(() => dok.Stan = StanDokumentuHandlowego.Zatwierdzony);
var guid = dok.Guid;
// 3. Dopiero Save() księguje obroty/zasoby. SaveDispose zamyka okno edycji sesji.
SaveDispose();
return guid;
}
[Test]
[Description("W12: zatwierdzenie przychodu (PW) zmienia stan na Zatwierdzony i tworzy zasoby po Save.")]
public void W12_ZatwierdzeniePrzychodu_UstawiaStanIKsięgujeZasoby()
{
// Tworzymy PW z pozycją (przychód — bez ryzyka stanu ujemnego).
var dok = UtworzDokument(Definicje.PrzyjecieWewnetrzne, magazyn: Magazyn(Magazyn_.Firma));
InTransaction(() => DodajPozycje(dok, Towar(Towar_.Bikini), ilosc: 10, cena: 5));
// Przed zatwierdzeniem dokument jest w buforze.
dok.Bufor.Should().BeTrue();
dok.Zatwierdzony.Should().BeFalse();
// Zatwierdzenie: bufor -> zatwierdzony (czytamy pole kalkulowane, nie enum).
InTransaction(() => dok.Stan = StanDokumentuHandlowego.Zatwierdzony);
var guid = dok.Guid;
// Dopiero Save() księguje obroty/zasoby/płatności.
SaveDispose();
// Odczyt na świeżej sesji po Guid (wzorzec zapis -> odczyt).
var zapisany = Get(guid);
zapisany.Zatwierdzony.Should().BeTrue();
zapisany.Bufor.Should().BeFalse();
// Przychód utworzył zasoby magazynowe (widoczne po Save).
zapisany.Zasoby.Count.Should().BeGreaterThan(0);
}
[Test]
[Description("W13: cofnięcie zatwierdzonego dokumentu bez zależności z powrotem do bufora.")]
public void W13_CofniecieDoBufora_PrzywracaStanBufor()
{
// Zatwierdzony PW bez dokumentów podrzędnych.
var guid = UtworzZatwierdzonyPwIZapisz();
// Re-get na świeżej sesji (po SaveDispose nie wolno edytować obiektu z poprzedniej sesji — §8).
var dok = Get(guid);
dok.Zatwierdzony.Should().BeTrue();
// Cofnięcie: zatwierdzony -> bufor (odksięgowanie przy Save).
InTransaction(() => dok.Stan = StanDokumentuHandlowego.Bufor);
SaveDispose();
var poCofnieciu = Get(guid);
poCofnieciu.Bufor.Should().BeTrue();
poCofnieciu.Zatwierdzony.Should().BeFalse();
}
[Test]
[Description("W14: anulowanie dokumentu w buforze ustawia stan Anulowany, rekord pozostaje w bazie.")]
public void W14_AnulowanieZBufora_UstawiaStanAnulowany()
{
// PW w buforze (anulowanie z bufora nie wymaga odksięgowania).
var dok = UtworzDokument(Definicje.PrzyjecieWewnetrzne, magazyn: Magazyn(Magazyn_.Firma));
InTransaction(() => DodajPozycje(dok, Towar(Towar_.Bikini), ilosc: 10, cena: 5));
dok.Bufor.Should().BeTrue();
// Anulowanie: bufor -> anulowany.
InTransaction(() => dok.Stan = StanDokumentuHandlowego.Anulowany);
var guid = dok.Guid;
SaveDispose();
// Po anulowaniu rekord nadal istnieje (w przeciwieństwie do Delete) i jest oznaczony jako anulowany.
var zapisany = Get(guid);
zapisany.Should().NotBeNull();
zapisany.Anulowany.Should().BeTrue();
zapisany.Bufor.Should().BeFalse();
}
[Test]
[Description("W14: anulowanie zatwierdzonego przychodu odksięgowuje zasoby, rekord zostaje.")]
public void W14_AnulowanieZatwierdzonego_OdksięgowujeIRekordZostaje()
{
// Zatwierdzony PW (utworzył zasoby).
var guid = UtworzZatwierdzonyPwIZapisz();
var dok = Get(guid);
dok.Zatwierdzony.Should().BeTrue();
// Anulowanie zatwierdzonego: odksięgowanie skutków magazynowych przy Save.
InTransaction(() => dok.Stan = StanDokumentuHandlowego.Anulowany);
SaveDispose();
var zapisany = Get(guid);
// Rekord zachowany (numeracja/audyt), oznaczony jako anulowany.
zapisany.Should().NotBeNull();
zapisany.Anulowany.Should().BeTrue();
// Anulowanie odksięgowało zasoby utworzone przez przychód.
zapisany.Zasoby.Count.Should().Be(0);
}
[Test]
[Description("W16: usunięcie dokumentu w buforze bez zależności (Delete) trwale kasuje rekord.")]
public void W16_UsuniecieZBufora_KasujeRekord()
{
// Dokument w buforze, bez powiązań i rezerwacji — usunięcie dozwolone.
var dok = UtworzDokument(Definicje.PrzyjecieWewnetrzne, magazyn: Magazyn(Magazyn_.Firma));
InTransaction(() => DodajPozycje(dok, Towar(Towar_.Bikini), ilosc: 10, cena: 5));
var guid = dok.Guid;
// Warunki bezpiecznego usunięcia: bufor.
dok.Bufor.Should().BeTrue();
// Twarde usunięcie (kasuje też pozycje) w tej samej sesji edycyjnej, bez wcześniejszego
// SaveDispose — Delete musi nastąpić na żywym obiekcie, przed zapisem.
InTransaction(() => dok.Delete());
SaveDispose();
// Po usunięciu indeksator po Guid rzuca RowNotFoundException dla nieistniejącego GUID (§5).
Assert.Throws(() =>
{
var _ = Get(guid);
});
}
[Test]
[Description("W16: anulowanie jako alternatywa dla usunięcia zatwierdzonego — rekord pozostaje.")]
public void W16_ZatwierdzonyAnulowanyZamiastUsuniety_RekordZostaje()
{
// Zatwierdzonego dokumentu nie można usuwać przez Delete (tylko bufor) —
// zalecaną ścieżką dla nieodwracalnego wycofania jest anulowanie (zachowuje numer i audyt).
var guid = UtworzZatwierdzonyPwIZapisz();
var dok = Get(guid);
// Poza buforem — Delete jest zabronione, więc anulujemy.
dok.Bufor.Should().BeFalse();
InTransaction(() => dok.Stan = StanDokumentuHandlowego.Anulowany);
SaveDispose();
// Rekord nadal w bazie, oznaczony jako anulowany.
var zapisany = Get(guid);
zapisany.Should().NotBeNull();
zapisany.Anulowany.Should().BeTrue();
}
[Test]
[Description("W15: PoprawaStanuDokumentuWorker na poprawnym dokumencie nie zmienia jego stanu.")]
public void W15_NaprawaStanu_NaPoprawnymDokumencie_ZachowujeStan()
{
// Zatwierdzony, spójny dokument — naprawa stanu nie powinna nic zepsuć.
var guid = UtworzZatwierdzonyPwIZapisz();
var dok = Get(guid);
dok.Zatwierdzony.Should().BeTrue();
// Worker sam zarządza transakcją wewnątrz NaprawStan() — ustawiamy tylko kontekst.
var naprawa = new PoprawaStanuDokumentuWorker { Dokument = dok };
naprawa.NaprawStan();
// Wystarczy Save() po akcji, by utrwalić ewentualne zmiany workera.
SaveDispose();
// Dokument poprawny — stan po naprawie pozostaje zatwierdzony.
var poNaprawie = Get(guid);
poNaprawie.Zatwierdzony.Should().BeTrue();
}
[Test]
[Description("W15: PrzeliczenieStanuWorker w trybie SprawdzićPoprawność (diagnostyka) nie zmienia danych.")]
public void W15_SprawdzeniePoprawnosciObrotow_NieZmieniaStanu()
{
// Zatwierdzony przychód z poprawnymi obrotami.
var guid = UtworzZatwierdzonyPwIZapisz();
var dok = Get(guid);
var zasobyPrzed = dok.Zasoby.Count;
// Tryb SprawdzićPoprawność tylko raportuje (Trace) — nie commituje zmian.
// Worker sam otwiera transakcje wewnątrz PrzeliczStan(); nie owijamy go własnym Logout.
var sprawdz = new PrzeliczenieStanuWorker(
PrzeliczenieStanuWorker.Opcje.SprawdzićPoprawność,
wszystkieMagazyny: false, rozchód0: false, przywracajWartość: true) { Dokument = dok };
sprawdz.PrzeliczStan();
// Tryb diagnostyczny nie modyfikuje danych — stan i zasoby bez zmian.
dok.Zatwierdzony.Should().BeTrue();
dok.Zasoby.Count.Should().Be(zasobyPrzed);
}
}